Power of Java MemoryMapped File
Power of Java MemoryMapped File
In JDK 1.4 an interesting feature of Memory mapped file was added to Java, which allows to map any file to OS memory for efficient reading. A memory mapped file can be used to develop an IPC type of solution. This article is an experiment with memory mapped file to create IPC.
Some details about Memory Mapped File, definition from WIKI
A memory-mapped file is a segment of virtual memory which has been assigned a direct byte-for-byte correlation with some portion of a file or file-like resource. This resource is typically a file that is physically present on-disk, but can also be a device, shared memory object, or other resource that the operating system can reference through a file descriptor. Once present, this correlation between the file and the memory space permits applications to treat the mapped portion as if it were primary memory.
Sample Program
Below we have two Java programs, one is a writer and the other is a reader. The writer is the producer and tries to write to Memory Mapped file, the reader is the consumer and it reads messages from the memory mapped file. This is just a sample program to show you the idea, it does’t handle many edge cases but it is good enough to build something on top of a memory mapped file.
MemoryMapWriter
import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; public class MemoryMapWriter { public static void main(String[] args) throws FileNotFoundException, IOException, InterruptedException { File f = new File("c:/tmp/mapped.txt"); f.delete(); FileChannel fc = new RandomAccessFile(f, "rw").getChannel(); long bufferSize=8*1000; MappedByteBuffer mem =fc.map(FileChannel.MapMode.READ_WRITE, 0, bufferSize); int start = 0; long counter=1; long HUNDREDK=100000; long startT = System.currentTimeMillis(); long noOfMessage = HUNDREDK * 10 * 10; for(;;) { if(!mem.hasRemaining()) { start+=mem.position(); mem =fc.map(FileChannel.MapMode.READ_WRITE, start, bufferSize); } mem.putLong(counter); counter++; if(counter > noOfMessage ) break; } long endT = System.currentTimeMillis(); long tot = endT - startT; System.out.println(String.format("No Of Message %s , Time(ms) %s ",noOfMessage, tot)) ; } }
MemoryMapReader
import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; public class MemoryMapReader { /** * @param args * @throws IOException * @throws FileNotFoundException * @throws InterruptedException */ public static void main(String[] args) throws FileNotFoundException, IOException, InterruptedException { FileChannel fc = new RandomAccessFile(new File("c:/tmp/mapped.txt"), "rw").getChannel(); long bufferSize=8*1000; MappedByteBuffer mem = fc.map(FileChannel.MapMode.READ_ONLY, 0, bufferSize); long oldSize=fc.size(); long currentPos = 0; long xx=currentPos; long startTime = System.currentTimeMillis(); long lastValue=-1; for(;;) { while(mem.hasRemaining()) { lastValue=mem.getLong(); currentPos +=8; } if(currentPos < oldSize) { xx = xx + mem.position(); mem = fc.map(FileChannel.MapMode.READ_ONLY,xx, bufferSize); continue; } else { long end = System.currentTimeMillis(); long tot = end-startTime; System.out.println(String.format("Last Value Read %s , Time(ms) %s ",lastValue, tot)); System.out.println("Waiting for message"); while(true) { long newSize=fc.size(); if(newSize>oldSize) { oldSize = newSize; xx = xx + mem.position(); mem = fc.map(FileChannel.MapMode.READ_ONLY,xx , oldSize-xx); System.out.println("Got some data"); break; } } } } } }
Observation
Using a memory mapped file can be a very good option for developing Inter Process communication, throughput is also reasonably well for both produce & consumer. Performance stats by run producer and consumer together:
Each message is one long number
Produce – 10 Million message – 16(s)
Consumer – 10 Million message 0.6(s)
A very simple message is used to show you the idea, but it can be any type of complex message, but when there is complex data structure then serialization can add to overhead. There are many techniques to get over that overhead. More in next blog.
Ashkrit, Thanks again for a great article. The example is straight forward and very clear. A couple of questions: 1. Are you assuming that MappedByteBuffer is always direct? I read elsewhere http://javarevisited.blogspot.com/2012/01/memorymapped-file-and-io-in-java.html that it can be non-direct also. However, looking at the source code of MappedByteBuffer, it clearly says that its direct buffer backed. A bit confused on that. Could you pls. shed some light on it? 2. You have spoken about IPC in this article. By giving reading/writing examples in separate files, are you alluding to IPC? What use cases do you see in practical implementation? How will the… Read more »
Hi Ashley, Thanks for interest in my blog. 1 – Yes in java MappedByteBuffer is always direct, there is only one implementation(DirectByteBuffer) available in java for MappedByteBuffer. I did cross check in JDK 8, but nothing has changed on this. So it is always direct. 2 – Yes in the atricle, it is the simple example of IPC. There are many use case of IPC, If you have some producer/consumer type of problem or some kind of request dispatch to service. Reliable persistence is one of the very common use case why you will use memorymapped file and then you… Read more »
Hi Ashkrit, Thanks a lot for a nice intro article on a memory-mapped files in Java. I have the following question for you and a bit of an observation If you start with an empty file, and run the reader code only, it will execute the FileChannel fc = new RandomAccessFile(new File(“c:/tmp/mapped.txt”), “rw”).getChannel(); long bufferSize=8*1000; MappedByteBuffer mem = fc.map(FileChannel.MapMode.READ_ONLY, 0, bufferSize); Which will allocate 1024 bytes, therefore making the buffer filled with 0 bytes. We will read the first 1024 empty bytes until hitting the point when we wait for a new message, which is incorrect in my opinion. Also,… Read more »
Hi Maciej,
Your observation is correct regarding reader, sample program that i used in blog to demonstrate how to use memory mapped file for IPC.
There are better way to do same thing , ideally index file is used to check whether data is available or not. Write to memorymapped file uses java memory model, so easy to get write guarantee.
I think i should write blog on this soon.
I am not sure if you have looked into chronical .
Chronical is based on memory mapped file and it has some interesting implementation of IPC.
I’d just like to add that I’ve created a library called MappedBus (http://github.com/caplogic/mappedbus) which helps with both the problem of synchronizing a reader with the writer and also allows for multiple Java processes (JVMs) to write records to the same memory mapped file in order. For this it makes use of Unsafe, CAS and volatile fields. The full explanation of how it’s done is at the github page if you’re interested.
Great, i will have look at it.
hi Ashkrit Sharma
i have a use with mmap files for high volume data processing. please drop a mail @.kalva@inmobi.com or give me ur mail id i will get in touch with you.
It is a good example on the topic to start with.
I’m using ffmpeg in android project, it wants input and output for processing videos ,but I want the input and output to be buffers (or any thing in memory) ,can you help me how to achieve this? Actually I want to read and write from memory… I think I should get the address of buffers in memory and it is related to mem protocol …. this is one case I think it shows my problem: First I want to extract the frames of a gif Second process each frame with android SDK classes like bitmaps,paints and… Third merge frames and… Read more »