Memory-Mapped Files in Java

Naveen AnamaneniUpdated · 4 min read

A memory-mapped file is a file that the operating system makes appear directly in the program's memory. Instead of calling read and write, you get a MappedByteBuffer and read or change bytes in it like an array; the operating system loads pages of the file when they're touched and writes changes back by itself. You create one with FileChannel.map. Memory mapping is very fast for large files and random access, and it lets two programs share the same data. In this lesson, you will learn how to map a file and when it's worth it.

How to Map a File

Syntax
try (FileChannel ch = FileChannel.open(path, READ, WRITE)) {
    MappedByteBuffer map = ch.map(FileChannel.MapMode.READ_WRITE, 0, size);
    map.put(index, value);       // writes go straight to the mapped file
}
Map modeMeaning
READ_ONLYIt is used to read the file; writing throws.
READ_WRITEChanges are written back to the file.
PRIVATEChanges stay in memory and never reach the file (copy-on-write).

Example: Updating a File Through Memory

Let's see an example that maps a counter file and changes numbers in place.

Mapped.java▶ Try it
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
 
public class Mapped {
    public static void main(String[] args) throws IOException {
        Path file = Path.of("counters.dat");
        int counters = 4;
 
        try (FileChannel ch = FileChannel.open(file,
                StandardOpenOption.CREATE, StandardOpenOption.READ,
                StandardOpenOption.WRITE)) {
            MappedByteBuffer map = ch.map(
                FileChannel.MapMode.READ_WRITE, 0, counters * 4L);
 
            for (int round = 0; round < 1000; round++) {
                int slot = round % counters;
                map.putInt(slot * 4, map.getInt(slot * 4) + 1);
            }
            map.force();
            System.out.println("File size: " + ch.size() + " bytes");
        }
 
        try (FileChannel ch = FileChannel.open(file)) {
            MappedByteBuffer map = ch.map(FileChannel.MapMode.READ_ONLY,
                                          0, ch.size());
            StringBuilder values = new StringBuilder();
            for (int i = 0; i < counters; i++) {
                values.append(map.getInt(i * 4)).append(' ');
            }
            System.out.println("Counters: " + values.toString().strip());
        }
    }
}
Output
File size: 16 bytes
Counters: 250 250 250 250

Explanation: Mapping 16 bytes in READ_WRITE mode also grew the new file to 16 bytes. getInt(i) and putInt(i, v) read and write directly at byte offsets, with no read or write calls. force() asks the operating system to save the changes to disk. Reopening the file with a read-only map shows the four counters, each increased 250 times.

When to Use Memory Mapping

Good fitPoor fit
Very large files read in random orderSmall files read once from start to end
Databases, search indexes, cachesFiles on network drives
Sharing data between processesFiles whose size changes often

Common Mistakes

Mapping more than 2 GB in one MappedByteBuffer. Its index is an int; map large files in several regions, or use MemorySegment.

Writing to a READ_ONLY map. It throws ReadOnlyBufferException.

Assuming changes are on disk immediately. Call force() when durability matters.

Practice

  1. Easy: Map a text file read-only and count how many bytes are the letter a.
  2. Harder: Create a 100 MB file, then time summing every 4096th byte with a mapped buffer and with a RandomAccessFile using seek.

Quick check: Memory-mapped files

4 questions
1Which method creates a memory-mapped buffer?
2Which map mode keeps changes out of the file?
3What does force() do?
4When is memory mapping a good choice?

Key Takeaways

Was this tutorial helpful?

Lesson 368 of 482