对于两个JVM(运行在同一台物理机器上),Java中是否有一种使用/共享相同内存地址空间的方法?假设JVM-1中的生产者将消息放置在特定的预定义内存位置,如果JVM-2上的使用者知道要查看哪个内存位置,它能检索消息吗?
发布于 2014-08-20 05:25:26
解决方案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." );
}
}
客户端:
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' );
}
}
解决方案2:
另一种解决方案是使用Java 插座在进程之间来回通信。这有一个额外的好处,就是可以很容易地在网络上进行通信。可以说,这比使用内存映射文件要慢,但我没有任何基准来支持该语句。我不会发布代码来实现这个解决方案,因为实现一个可靠的网络协议会变得非常复杂,并且是相当特定于应用程序的。有许多好的网站,可以找到快速搜索。
现在,如果您想在两个不同的进程之间共享内存,那么上面的示例就是。如果您只想在当前进程中读取/写入任意内存,那么首先应该知道一些警告。这违反了JVM的整个原则,您真的不应该在生产代码中这样做。如果不小心,您就会破坏所有的安全,并且很容易使JVM崩溃。
尽管如此,这是相当有趣的实验。要在当前进程中对任意内存进行读写,可以使用sun.misc.Unsafe
类。这是在我所知道和使用过的所有JVM上提供的。有关如何使用该类的示例可以找到这里。
发布于 2016-07-10 09:41:03
有一些IPC库可以通过Java中的内存映射文件来方便共享内存的使用.
Chronicle类似于一个非阻塞的Queue
,但您可以在一个JVM中提供消息,然后在另一个JVM中轮询它。
在这两个JVM中,您应该在同一个FS目录中创建一个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
}
}
副翼不仅仅是IPC队列(它是一个网络通信框架),但它也提供了IPC功能。它类似于纪事队列,一个重要的区别是它使用SBE库来进行消息编组/脱卸,而纪事队列使用纪事线材。
Chronicle允许IPC通过某些键进行通信。在这两个JVM中,您应该创建一个配置相同的映射,并将其持久化到同一个文件(如果您不需要实际的磁盘持久化(例如,在/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
}
发布于 2016-07-10 10:06:46
缓存是解决需求的最佳解决方案。
在计算中,分布式缓存是传统缓存概念在单个区域中使用的扩展。分布式缓存可以跨越多个服务器,从而使其在大小和跨国容量上都能增长。
没有几种选择:
兵马俑允许JVM集群中的线程通过JVM边界相互交互,使用相同的内置JVM设施扩展为具有集群范围的含义。
一致性是一种专有的基于1Java的内存数据网格,与传统的关系数据库管理系统相比,它具有更好的可靠性、可扩展性和性能。
Ehcache是一个广泛使用的开源,用于通用缓存、Java和轻量级容器。它具有内存和磁盘存储、复制和无效复制、侦听器、缓存加载器、缓存扩展、缓存异常处理程序、gzip缓存servlet筛选器、RESTful和SOAP APIs。
Redis是一种数据结构服务器。它是开源的,联网的,内存中的,并且存储具有可选持久性的密钥.
服务器是一个开放源码、分布式(无共享架构)的面向NoSQL文档的多模型数据库软件包,是针对交互式应用程序进行优化的。这些应用程序可以通过创建、存储、检索、聚合、操作和显示数据为许多并发用户服务。
有用的员额:
信息Q文章
https://stackoverflow.com/questions/25396664
复制相似问题