Shared Memory between two JVMs

52,044

Solution 1

Solution 1:

The best solution in my opinion is to use memory mapped files. This allows you to share a region of memory between any number of process, including other non java programs. You can't place java objects into a memory mapped file, unless you serialize them. The following example shows that you can communicate between two different process, but you would need to make it much more sophisticated to allow better communication between the processes. I suggest you look at Java's NIO package, specifically the classes and methods used in the below examples.

Server:

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." );
    }
}

Client:

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

}

Solution 2:

Another solution is to use Java Sockets to communicate back and forth between processes. This has the added benefit of allowing communication over a network very easily. It could be argued that this is slower than using memory mapped files, but I do not have any benchmarks to back that statement up. I won't post code to implementing this solution, as it can become very complicated to implement a reliable network protocol and is fairly application specific. There are many good networking sites that can be found with quick searches.


Now the above examples are if you want to share memory between two different process. If you just want to read/write to arbitrary memory in the current process, there are some warnings you should know first. This goes against the entire principle of the JVM and you really really should not do this in production code. You violate all safety and can very easily crash the JVM if you are not very careful.

That being said, it is quite fun to experiment with. To read/write to arbitrary memory in the current process you can use the sun.misc.Unsafe class. This is provided on all JVMs that I am aware of and have used. An example on how to use the class can be found here.

Solution 2

There are some IPC libraries which facilitate use of shared memory via memory-mapped files in Java.

Chronicle-Queue

Chronicle Queue is similar to a non-blocking Java Queue, except you could offer a message in one JVM and poll it in another JVM.

In both JVMs you should create a ChronicleQueue instance in the same FS directory (locate this directory in a memory-mounted FS if you don't need message persistence):

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

Write a message in one JVM:

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

Read a message in another 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 is more than just IPC queue (it is a network communication framework), but it provides an IPC functionality as well. It is similar to Chronicle Queue, one important difference is that it uses SBE library for message marshalling/demarshalling, while Chronicle Queue uses Chronicle Wire.

Chronicle Map

Chronicle Map allows IPC communication by some key. In both JVMs, you should create a map with identical configurations and persisted to the same file (the file should be localed in memory-mounted FS if you don't need actual disk persistence, e. g. in /dev/shm/):

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

Then in one JVM you could write:

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

On the reciever JVM:

Message message = ipc.remove(key);
if (message != null) {
    // process the message here
}

Solution 3

Distributed_cache is best solution to address your requirements.

In computing, a distributed cache is an extension of the traditional concept of cache used in a single locale. A distributed cache may span multiple servers so that it can grow in size and in transnational capacity.

Few options:

Terracotta allows threads in a cluster of JVMs to interact with each other across JVM boundaries using the same built-in JVM facilities extended to have a cluster-wide meaning

Oracle_Coherence is a proprietary1 Java-based in-memory data grid, designed to have better reliability, scalability and performance than traditional relational database management systems

Ehcache is a widely used open source Java distributed cache for general purpose caching, Java EE and light-weight containers. It features memory and disk stores, replicate by copy and invalidate, listeners, cache loaders, cache extensions, cache exception handlers, a gzip caching servlet filter, RESTful and SOAP APIs

Redis is a data structure server. It is open-source, networked, in-memory, and stores keys with optional durability.

Couchbase_Server is an open-source, distributed (shared-nothing architecture) multi-model NoSQL document-oriented database software package that is optimized for interactive applications. These applications may serve many concurrent users by creating, storing, retrieving, aggregating, manipulating and presenting data.

Useful posts:

What is Terracotta?

Is Terracotta a distributed cache?

infoq article

Solution 4

Honestly, you don't want to share the same memory. You should send only the data that you need to the other JVM. That being said, in the case you do need the shared memory, other solutions exist.

Sending Data Two JVMs do not share the same memory access points, so it is impossible to use a reference from one JVM to use in another. A new reference will simply be create because they don't know about each other.

However, you may ship the data to the other JVM, and back in a variety of ways:

1) Using RMI, you can setup a remote server to parse data. I found it a bit of a hassle to set up because it requires security changes and that the data be Serializable. You can find out more at the link.

2) Using a server is the age-old method of sending data to different places. One way to implement this is using a ServerSocket and connecting with a Socket on localhost. Objects still need to be Serializable if you want to use ObjectOutputStream.


Sharing Data This is very dangerous and volatile, low-level, and, well, unsafe (literally).

If you want to use Java code, you can take a look at using s.m.Unsafe, using the correct memory addresses, you will be able to retrieve Objects stored by the backing C/C++ arrays in the OS.

Otherwise, you can use native methods to access the C/C++ arrays yourself, although I have no clue how this could be implemented.

Solution 5

Jocket, an experimental project I made a few years ago does exactly this.

It includes a drop-in replacement for java.net.Socket and java.net.ServerSocket if you want to use Input/OutputStream.

Each directional channel uses a pair of circular buffers to post and get data (one for the "packets" and one for the address of packets). The buffers are obtained through a RandomAccessFile.

It includes a small JNI layer (linux) to implement IPC synchronization (i.e. notify the other process of availability of data) but this is not mandatory if you want to poll for data.

Share:
52,044

Related videos on Youtube

OneWorld
Author by

OneWorld

Updated on July 09, 2022

Comments

  • OneWorld
    OneWorld almost 2 years

    Is there a way in Java, for two JVMs (running on same physical machine), to use/share the same memory address space? Suppose a producer in JVM-1 puts messages at a particular pre-defined memory location, can the consumer on JVM-2 retrieve the message if it knows which memory location to look at?

    • Elliott Frisch
      Elliott Frisch over 9 years
      No. You cannot access arbitrary memory in Java. But, you can share memory between two JVMs. Use JNI and ipcs. Or sockets over loopback.
    • MadProgrammer
      MadProgrammer over 9 years
      AFAIK, there's nothing built into the core API. You could use Sockets to communicate between each other or even via a third party
    • prashant thakre
      prashant thakre over 9 years
      who JVMs or two JVMs??? please correct the question title.
    • Scary Wombat
      Scary Wombat over 9 years
      No, even if this was one JVM you can not access a pre-defined memory location. You can share memory space by using a multi-tenanted JVM like waratek
    • Manjunath
      Manjunath over 9 years
      You can think of serializing the objects into a file and refer it from the other jvm
    • Siva Kumar
      Siva Kumar over 9 years
      What is your purpose of doing. If you want to share info between two jvms , then you can go for a RMI.
    • Gustav Grusell
      Gustav Grusell over 9 years
      Depending on the use case, it may be possible to use memory mapped IO to achieve something roughly similar to shared memory. See javacodegeeks.com/2013/05/power-of-java-memorymapped-file.ht‌​ml
    • chrylis -cautiouslyoptimistic-
      chrylis -cautiouslyoptimistic- over 9 years
      Why do you want to do this? If it's so performance-critical that a Unix socket won't work, Java is probably the wrong choice.
  • Matthieu
    Matthieu over 8 years
    "arbitrary memory locations", yes, as long as you stay within the same process. No OS will let any process read memory from another process! (except for some specific embedded OS). The paging is not the same: 0x3f7e is not the same physical address for all processes.
  • Xabster
    Xabster over 8 years
    @Matthieu: completely untrue. You can read arbitrary memory locations completely unrestricted.
  • Matthieu
    Matthieu over 8 years
    Did you try your solution? There are ways to hack into another process memory (see that other question) but it is very OS specific and needs special privileges. In the end, as you note, it is highly unrecommended. Moreover, the JNI-side has a different memory mapping than the Java-side (arrays can be copied back and forth), which makes it even more difficult to compute the correct hacking address.
  • Xabster
    Xabster over 8 years
    @Matthieu: Hacking address? You'd use JNI in both Java programs to call the implementation of the header I gave. One will write, one will read. Is this location what you call "hacking address"?
  • Matthieu
    Matthieu over 8 years
    What I mean is the two JVM will use different virtual address spaces so data at address e.g. 0x3f7e in JVM1 is not the same as data at address 0x3f7e in JVM2. From JVM2, if you want to read data from the heap of JVM1, you should get JVM1 PID, copy its heap locally (if you get the permission) and read the data you want at an address that would probably be 0x3f7e but maybe not. That's what I call "hacking address" (0x3f7e in JVM1 seen from JVM2 can be something different from 0x3f7e).
  • Matthieu
    Matthieu over 8 years
    Now that you mention it I'm thinking about opening a new question on that specific topic, I think it is highly OS-dependent :)
  • WestCoastProjects
    WestCoastProjects over 7 years
    @vach Care to teach us here how to write to shared memory accessible to different processes via pure java ? If not then lose the attitude.
  • vach
    vach over 7 years
    Basically the access to shared memory world comes with RandomAccessFile, which allows you to write randomly from process A, but for process B to see your write, it needs to have some sort of busy loop that checks the memory for changes... its up to you to decide how fast you want to see changes, fastest being no delay loop, that will be fastest possible way for 2 processes to communicate via shared memory. However if you dont want to spend some processing power on that, you can simply put some microsecond sleep delay in loop.
  • vach
    vach over 7 years
    Peter Lawrey did a lot in this area. I learned most of what i know from his videos. Note that this sort of speed is 90% not needed in most applications, but in HFT world its a necessity. I was very surprised when i found out that people are doing with java same stuff that was done with C++ before...
  • vach
    vach over 7 years
    If you want to go down the rabbit hole here is where you start 1. youtube.com/watch?v=JMEVi_t38uc 2. youtube.com/watch?v=lWvZDZ-oRt0
  • vach
    vach over 7 years
    People who code in low level world fail to realize that nowadays most of their expert knowledge is applied automatically in JVM as it will optimize an amateur code to a very fast and efficient assembly. Something that really experienced low level engineer will do with significant effort, now is done by any average java developer without him knowing all the low level tricks. However java still has things to do to fully match with Cpp, like value types... This is why performance penalty for using high level language goes down over time.
  • Qoros
    Qoros almost 7 years
    Can you give an example C client to read the data of the Server?
  • Smith_61
    Smith_61 almost 7 years
    @Qoros C would require OS specific APIs to deal with memory mapped files, such as mmap or VirtualAlloc. Otherwise the code would look fairly similar. Open the file, map it into the process, then read and write to the returned pointer.
  • Vince
    Vince over 6 years
    Unfortunately, this won't work. The address space for each process is virtual. That is, process one's address 0x400000 will not map to the same physical address as process two's address 0x400000. So it is not possible to map addresses directly between two processes in the way you are suggesting. The way to do it is via memory-mapped files, as the accepted answer explains.
  • cleberz
    cleberz almost 6 years
    @Xabster the .dlls will have different addressing spaces (hence different data) and the only way to share memory through them is by using a memory-mapped file, which comes associated with synchronization and etc. I suppose through running a quick sample you might end up thinking you're reading data from other processes, but in reality all you're reading is junk left over from discarded page memories that are reused by the .dll. Also, global static variables in .dlls are copied into each process at startup.
  • Jack
    Jack over 4 years
    So basically the poster thinks that in C this code: int address = 0x01234; int value = *address; will read the value that is stored at physical address 0x01234? Basic security in modern OS (where modern here means > 1990) is based on logical to physical address mapping, and no user-level application can access random physical address.
  • dan.m was user2321368
    dan.m was user2321368 about 4 years
    Down vote for exactly the reason in @Vince's comment
  • dan.m was user2321368
    dan.m was user2321368 about 4 years
    No modern OS will allow one process to access the memory address space of another. As the address space of a process is virtual, the actual memory which one process addresses as 0xABCD is different than what any other process addresses as 0xABCD. The only way for two processes to communicate is with some mechanism explicitly provided by the OS for that purpose (pipes, memory mapped files, etc.). Of those, java only supports memory mapped files.
  • nullsector76
    nullsector76 almost 3 years
    so is this creating a physical file? I don't want to kill people's hard drives by constantly streaming read/write onto a disk
  • Stefan Reich
    Stefan Reich almost 2 years
    @nullsector76 Exactly (yes, it is creating a physical file). This is keeping me from using this too. You could use a RAM disk, but I don't think there is a portable way to create a RAM disk in Java. Also, how do you signal something to the other process, e.g. when new data was written, and for synchronisation (if you need that)? There is no mechanism for that either, so you would need to use some sockets additionally.