两个JVM之间的共享内存

One*_*rld 32 java jvm memory-management

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

Smi*_*_61 28

解决方案1:

我认为最好的解决方案是使用内存映射文件.这允许您在任意数量的进程(包括其他非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." );
    }
}
Run Code Online (Sandbox Code Playgroud)

客户:

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' );
    }

}
Run Code Online (Sandbox Code Playgroud)

解决方案2:

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


现在上面的例子是你想要在两个不同的进程之间共享内存.如果您只想在当前进程中读取/写入任意内存,则应首先了解一些警告.这违背了JVM的整个原则,你真的不应该在生产代码中这样做.您违反了所有安全措施,如果您不是非常小心,可能很容易导致JVM崩溃.

话虽这么说,试验很有趣.要读取/写入当前进程中的任意内存,您可以使用sun.misc.Unsafe该类.这是在我所知道并使用过的所有JVM上提供的.可以在此处找到有关如何使用该类的示例.


lev*_*tov 23

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

纪事队列

Chronicle Queue类似于非阻塞Java Queue,除了您可以在一个JVM中提供消息并在另一个JVM中轮询它.

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

ChronicleQueue ipc = ChronicleQueueBuilder.single("/dev/shm/queue-ipc").build();
Run Code Online (Sandbox Code Playgroud)

在一个JVM中编写消息:

ExcerptAppender appender = ipc.acquireAppender();
appender.writeDocument(w -> {
    w.getValueOut().object(message);
});
Run Code Online (Sandbox Code Playgroud)

在另一个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
    }
}
Run Code Online (Sandbox Code Playgroud)

Aeron IPC

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

纪事地图

Chronicle Map允许通过某些键进行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"));
Run Code Online (Sandbox Code Playgroud)

然后在一个JVM中你可以写:

ipc.put(key, message); // publish a message
Run Code Online (Sandbox Code Playgroud)

在接收器JVM上:

Message message = ipc.remove(key);
if (message != null) {
    // process the message here
}
Run Code Online (Sandbox Code Playgroud)


Rav*_*abu 5

Distributed_cache是满足您要求的最佳解决方案。

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

几种选择:

Terracotta允许使用相同的内置JVM功能扩展JVM群集中的线程在JVM边界之间相互交互,以扩展其在群集范围内的含义。

Oracle_Coherence 是基于Java 的专有1内存中数据网格,其设计具有比传统关系数据库管理系统更好的可靠性,可伸缩性和性能。

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

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

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

有用的帖子:

什么是兵马俑?

Terracotta是分布式缓存吗?

infoq文章