两个JVM之间的共享内存

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

解决scheme1:

我认为最好的解决scheme是使用内存映射文件。 这允许你在任意数量的进程之间共享一个内存区域,包括其他非java程序。 除非您序列化它们,否则不能将java对象放置到内存映射文件中。 下面的例子说明你可以在两个不同的进程之间进行通信,但是你需要使它更加复杂,以便在进程之间进行更好的通信。 我build议你看看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' ); } } 

解决scheme2:

另一个解决scheme是使用Java 套接字在进程之间来回通信。 这具有允许通过networking进行通信的额外好处。 有人可能会认为这比使用内存映射文件慢,但我没有任何基准来支持这个说法。 我不会发布代码来实现这个解决scheme,因为实现一个可靠的networking协议会变得非常复杂,而且相当专用。 有很多很好的networking站点可以通过快速searchfind。


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

这就是说,这是相当有趣的尝试。 要读取/写入当前进程中的任意内存,可以使用sun.misc.Unsafe类。 这在我知道并使用的所有JVM上提供。 如何使用这个类的例子可以在这里find。

有一些IPC库可以通过Java中的内存映射文件方便地使用共享内存。

纪事队列

Chronicle Queue类似于非阻塞Java Queue ,只不过您可以在一个JVM中提供消息,并在另一个JVM中进行轮询。

在这两个JVM中,您都应该在同一个FS目录中创build一个ChronicleQueue实例(如果不需要消息持久化,请在内存安装的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

Aeron不仅仅是IPC队列(这是一个networking通信框架),而且还提供了IPCfunction。 它与Chronicle Queue类似,其中一个重要的区别是它使用SBE库进行消息编组/解编,而Chronicle队列使用Chronicle Wire 。

纪事报地图

Chronicle Map允许通过一些关键的IPC通讯。 在这两个JVM中,您应该创build一个具有相同configuration的映射,并将其保存到相同的文件中(如果不需要实际的磁盘持久性,例如在/dev/shm/ ,该文件应该位于内存安装的FS中):

 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 } 

老实说,你不想分享相同的记忆。 您应该只将需要的数据发送给其他JVM。 这就是说,如果你确实需要共享内存,还有其他解决scheme。

发送数据两个JVM不共享相同的内存访问点,所以不能使用来自一个JVM的引用在另一个JVM中使用。 一个新的参考将被简单地创build,因为他们不知道彼此。

但是,您可能会将数据发送到其他JVM,并以各种方式返回:

1)使用RMI ,你可以设置一个远程服务器来parsing数据。 我发现设置起来有点麻烦,因为它需要安全性更改,数据是可Serializable 。 你可以在链接find更多。

2)使用服务器是将数据发送到不同地方的古老方法。 实现这一点的一种方法是使用ServerSocket并在localhost上连接一个Socket 。 如果你想使用ObjectOutputStream对象仍然需要是可Serializable


共享数据这是非常危险和不稳定的,低层次的,也是不安全的(字面上的)。

如果你想使用Java代码,你可以看看使用smUnsafe ,使用正确的内存地址,你将能够检索OS中支持C / C ++数组存储的对象。

否则,你可以使用native方法来自己访问C / C ++数组,但我不知道如何实现。

是,

用一个中间程序,你可以写入和读取任意的内存位置。 你不能纯粹用Java来完成。

例如,您可以编写一段C ++代码,可以读取任意内存位置并通过JNI调用该代码。 与写入内存地址相反,情况也是如此。

首先为应该处理的类编写一个类定义,例如:

 public class MemTest { public native byte[] readMemory(int address); public native void writeMemory(int address, byte[] values); } 

然后你编译它。 然后你使用javah.exe(或者相当于linux)为它生成一个头文件:

 javah MemTest 

现在您可以编写一个包含该头文件的.cpp文件,并定义这些方法。 编译为DLL。 要加载.dll,请使用具有适当值的-Djava.library.path JVM参数或System.loadLibrary()。

请注意:我不build议这样做。 几乎肯定有更好的方法来做你想做的事情。

Distributed_cache是解决您的需求的最佳解决scheme。

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

几个选项:

Terracotta允许JVM集群中的线程通过JVM边界相互交互,使用扩展为具有集群范围含义的相同内置JVM工具

Oracle_Coherence是专有的1个基于Java的内存数据网格,与传统的关系数据库pipe理系统相比,具有更好的可靠性,可扩展性和性能

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

Redis是一个数据结构服务器。 它是开源的,联网的,在内存中,并存储可选耐用性的密钥。

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

有用的post:

什么是兵马俑?

Terracotta是分布式caching吗?

infoq文章

不支持枢轴堆内存

那么如何使用Unsafe将Object字节复制到off-head区域,然后如何将一个便宜的指针和类名称传递给第二个JVM,该JVM将使用指针和类名称将堆外空间复制并将其转换为in-第二个JVM中的堆对象。 它不是相同的对象实例,而是一个快速拷贝,没有序列化。

 public static Unsafe getUnsafe() { try { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); return (Unsafe)f.get(null); } catch (Exception e) { /* ... */ } } MyStructure structure = new MyStructure(); // create a test object structure.x = 777; long size = sizeOf(structure); long offheapPointer = getUnsafe().allocateMemory(size); getUnsafe().copyMemory( structure, // source object 0, // source offset is zero - copy an entire object null, // destination is specified by absolute address, so destination object is null offheapPointer, // destination address size ); // test object was copied to off-heap Pointer p = new Pointer(); // Pointer is just a handler that stores address of some object long pointerOffset = getUnsafe().objectFieldOffset(Pointer.class.getDeclaredField("pointer")); getUnsafe().putLong(p, pointerOffset, offheapPointer); // set pointer to off-heap copy of the test object structure.x = 222; // rewrite x value in the original object System.out.println( ((MyStructure)p.pointer).x ); // prints 777 .... class Pointer { Object pointer; } 

所以现在您将MyStructurep从((MyStructure)p.pointer).x传递到第二个JVM,您应该能够:

 MyStructure locallyImported = (MyStructure)p.pointer; 

我可以想象一个用例:假设有两个微服务可能运行在同一服务器上,也可能不运行在同一个服务器上,并且一个客户端策略(可能在容器AppServer中实现)知道服务在哪里部署,以防检测到请求的服务是在本地,它可能会使用不安全的服务客户端来透明地查询其他服务。 讨厌但有趣的,我想看到不使用networking的性能影响,绕过WebAPI(调用直接处理控制器),而不是序列化。 在这种情况下,除了控制器参数之外,应该提供控制器本身。 甚至没有想到安全。

https://highlyscalable.wordpress.com/2012/02/02/direct-memory-access-in-java/借用的代码片段;