当前位置: 首页 > 知识库问答 >
问题:

两个JVM之间的共享内存

慕嘉茂
2023-03-14

在Java中,对于两个JVM(运行在同一台物理机器上),是否有办法使用/共享相同的内存地址空间?假设JVM-1中的生产者将消息放在特定的预定义内存位置,如果JVM-2上的消费者知道要查看哪个内存位置,那么它是否可以检索消息?

共有3个答案

终弘厚
2023-03-14

分布式缓存是满足您需求的最佳解决方案。

在计算中,分布式缓存是在单个语言环境中使用的传统缓存概念的扩展。分布式缓存可能跨越多个服务器,因此它可以在规模和跨国容量上增长。

选项很少:

Terracotta允许JVM集群中的线程使用相同的内置JVM设施跨JVM边界相互交互,这些JVM设施被扩展为具有集群范围的含义

Oracle_Coherence是一个专有的基于Java的内存数据网格,旨在比传统的关系数据库管理系统具有更好的可靠性、可扩展性和性能

Ehcache是一种广泛使用的开源Java分布式缓存,用于通用缓存、Java EE和轻量级容器。它具有内存和磁盘存储、按拷贝复制和失效、侦听器、缓存加载程序、缓存扩展、缓存异常处理程序、gzip缓存servlet过滤器、RESTful和SOAP API

Redis是一个数据结构服务器。它是开源的、网络化的、在内存中的,并存储具有可选持久性的密钥。

Couchbase\u Server是一个开源、分布式(无共享体系结构)多模型NoSQL面向文档的数据库软件包,针对交互式应用程序进行了优化。这些应用程序可以通过创建、存储、检索、聚合、操作和呈现数据来服务于许多并发用户。

有用的帖子:

什么是赤陶?

Terracotta是分布式缓存吗?

Infoq文章

拓拔骁
2023-03-14

有一些IPC库通过Java中的内存映射文件来促进共享内存的使用。

编年史队列类似于非阻塞JavaQueue,只是您可以在一个JVM中提供消息并在另一个JVM中轮询它。

在这两个JVM中,您应该在同一个FS目录中创建一个历史队列实例(如果不需要消息持久性,请在内存装载的FS中找到此目录):

ChronicleQueue ipc = ChronicleQueueBuilder.single("/dev/shm/queue-ipc").build();

在一个JVM中编写一条消息:

ExcerptAppender appender = ipc.acquireAppender();
appender.writeDocument(w -> {
    w.getValueOut().object(message);
});

在另一个JVM中读取消息:

ExcerptTailer tailer = ipc.createTailer();
// If there is no message, the lambda, passed to the readDocument()
// method is not called.
tailer.readDocument(w -> {
    Message message = w.getValueIn().object(Message.class);
    // process the message here
});

// or avoid using lambdas
try (DocumentContext dc = tailer.readingDocument()) {
    if (dc.isPresent()) {
        Message message = dc.wire().getValueIn().object(Message.class);
        // process the message here
    } else {
        // no message
    }
}

Aeron不仅仅是IPC队列(它是一个网络通信框架),它还提供IPC功能。它类似于Chronicle队列,一个重要的区别是它使用SBE库进行消息编组/解编组,而Chronicle队列使用Chronicle Wire。

编年史映射允许通过某些键进行IPC通信。在两个JVM中,您应该创建一个具有相同配置的映射并持久化到同一个文件中(如果您不需要实际的磁盘持久化,则该文件应该定位在内存挂载的FS,例如在/dev/shm/中):

Map<Key, Message> ipc = ChronicleMap
    .of(Key.class, Message.class)
    .averageKey(...).averageValue(...).entries(...)
    .createPersistedTo(new File("/dev/shm/jvm-ipc.dat"));

然后在一个JVM中,您可以编写:

ipc.put(key, message); // publish a message

在接收者JVM上:

Message message = ipc.remove(key);
if (message != null) {
    // process the message here
}
彭烨烁
2023-03-14

在我看来,最好的解决方案是使用内存映射文件。这允许您在任意数量的进程(包括其他非java程序)之间共享内存区域。除非序列化java对象,否则无法将其放置到内存映射文件中。下面的示例显示,您可以在两个不同的流程之间进行通信,但您需要使其更加复杂,以便在流程之间进行更好的通信。我建议您看看Java的NIO包,特别是下面示例中使用的类和方法。

服务器:

public class Server {

    public static void main( String[] args ) throws Throwable {
        File f = new File( FILE_NAME );

        FileChannel channel = FileChannel.open( f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE );

        MappedByteBuffer b = channel.map( MapMode.READ_WRITE, 0, 4096 );
        CharBuffer charBuf = b.asCharBuffer();

        char[] string = "Hello client\0".toCharArray();
        charBuf.put( string );

        System.out.println( "Waiting for client." );
        while( charBuf.get( 0 ) != '\0' );
        System.out.println( "Finished waiting." );
    }
}

客户:

public class Client {

    public static void main( String[] args ) throws Throwable {
        File f = new File( FILE_NAME );
        FileChannel channel = FileChannel.open( f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE );

        MappedByteBuffer b = channel.map( MapMode.READ_WRITE, 0, 4096 );
        CharBuffer charBuf = b.asCharBuffer();

        // Prints 'Hello server'
        char c;
        while( ( c = charBuf.get() ) != 0 ) {
            System.out.print( c );
        }
        System.out.println();

        charBuf.put( 0, '\0' );
    }

}

另一种解决方案是使用Java套接字在进程之间来回通信。这样做的另一个好处是可以非常轻松地通过网络进行通信。可以说,这比使用内存映射文件慢,但我没有任何基准来支持这一说法。我不会发布实现此解决方案的代码,因为实现可靠的网络协议可能会变得非常复杂,并且是相当特定于应用程序的。有许多好的网络站点可以通过快速搜索找到。

现在,如果您想在两个不同的进程之间共享内存,则可以使用上面的示例。如果您只想在当前进程中读/写任意内存,那么您首先应该知道一些警告。这违背了JVM的整个原则,您真的不应该在生产代码中这样做。您违反了所有安全性,如果不小心,JVM很容易崩溃。

尽管如此,这是非常有趣的实验。要在当前进程中读取/写入任意内存,可以使用sun。杂项。不安全类。这是在我知道并使用过的所有JVM上提供的。这里可以找到一个关于如何使用该类的示例。

 类似资料:
  • 问题内容: 我在JVM(Scala)中构建了一个庞大的图形,我想反复使用它来调整算法。我不想每次都从磁盘重新加载它。有没有办法让它坐在一个JVM中,而又与另一个正在开发算法的JVM连接呢? 问题答案: 将图形保存到磁盘,然后使用MappedByteBuffer将其映射到内存。两个进程应使用相同的内存,该内存将与页面缓存共享。

  • 问题内容: 我需要在两个(或更多个)组合框之间共享数据,但是我想独立选择元素。例如,如果我在第一个comboBox中选择Object1,则我的第二个ComboBox也选择Object1,因为它们具有相同的模型(DefaultComboBoxModel,并且此模型还管理所选的对象)。但是我不想要这种行为。我想在我的comboBoxes中独立选择对象。当我在第一个comboBox中选择对象时,我的第二

  • 我正在试图弄清楚如何在Helm中的两个图表之间共享数据。 在两个图表之间共享configmap的最佳实践是什么?

  • 我有一个包含两个片段的FragmentActivity(support-v4)。并且都包含一个带有ActionBar的SearchView的菜单。 PlayListFragment: 附注。 我找到了原因,但没有解决方法: 呼叫 如何禁用ActionBar/SearchView/TextView的保存和还原?

  • 我有一个人[]有三个人(p1,p2,p3)。Person类有两个属性name和email。 我使用了以下代码。 但我不想这样用。我想使用两个组合框与相同的型号。我尝试使用DefaultComboBoxModel并重写getElementAt()方法,如下所示。 } 问题是如何使用相同的ComboBoxModel在一个JComboBox中添加Person[]的所有名称,并在另一个JComboBox中

  • 我很想知道Hibernate二级缓存是否可以在运行在两个不同JVM上的两个不同Hibernate会话之间共享。 我正在使用Hibernate 3.1和Ehcache来提供二级缓存,并且在Ehcache中。xml配置文件我们可以指定在磁盘上创建缓存的位置 因此,如果我现在在不同的JVM中打开两个不同的Hibernate会话,它们都指向同一个JVM,那么这将允许我在两个JVM之间共享二级缓存。 如果是