内存映射文件将磁盘文件直接映射到进程虚拟内存区域,文件内容对应一个字节数组。**操作系统按需分页加载**(典型页大小 4KB/8KB),访问未加载区域时触发缺页中断自动读入。
force() 强制同步)。RandomAccessFile file = new RandomAccessFile("abc.dat", "rw");
MappedByteBuffer buf = file.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, file.length());
file.close(); // 映射建立后文件句柄可关闭
MapMode:READ_ONLY、READ_WRITE、PRIVATE(写时复制,不反映到文件、不共享)。
继承 ByteBuffer,封装定长字节数组,维护 position 游标。
get()、put(byte[])、getInt()、putLong() 等,自动推进 position。getInt(index)、putLong(index, val),不改变 position。isLoaded()、load()(预加载)、force()(强制刷盘)。.data:定长记录,每条 1024 字节(4 字节长度 + 1020 字节体)。.meta:8 字节(4 字节 head 指针 + 4 字节 tail 指针),记录队列首尾在数据文件中的偏移量。head == tail 为空;(tail + MSG_SIZE) % DATA_SIZE == head 为满。
private static final int MAX_MSG_NUM = 1020 * 1024; // 最大消息数
private static final int MAX_MSG_BODY_SIZE = 1020; // 消息体最大长度
private static final int MSG_SIZE = MAX_MSG_BODY_SIZE + 4; // 单条占用
private static final int DATA_FILE_SIZE = MAX_MSG_NUM * MSG_SIZE; // ~1GB
private static final int META_SIZE = 8; // head + tail
public BasicQueue(String path, String queueName) throws IOException {
// 规范化路径
RandomAccessFile dataFile = new RandomAccessFile(path + queueName + ".data", "rw");
RandomAccessFile metaFile = new RandomAccessFile(path + queueName + ".meta", "rw");
try {
dataBuf = dataFile.getChannel().map(MapMode.READ_WRITE, 0, DATA_FILE_SIZE);
metaBuf = metaFile.getChannel().map(MapMode.READ_WRITE, 0, META_SIZE);
} finally {
// 映射建立后关闭文件句柄不影响映射
dataFile.close(); metaFile.close();
}
}
public void enqueue(byte[] data) throws IOException {
if (data.length > MAX_MSG_BODY_SIZE) throw new IllegalArgumentException("消息过大");
if (isFull()) throw new IllegalStateException("队列已满");
int tail = tail();
dataBuf.position(tail);
dataBuf.putInt(data.length);
dataBuf.put(data);
// 循环更新 tail
tail(tail + MSG_SIZE >= DATA_FILE_SIZE ? 0 : tail + MSG_SIZE);
}
public byte[] dequeue() throws IOException {
if (isEmpty()) return null;
int head = head();
dataBuf.position(head);
int length = dataBuf.getInt();
byte[] data = new byte[length];
dataBuf.get(data);
head(head + MSG_SIZE >= DATA_FILE_SIZE ? 0 : head + MSG_SIZE);
return data;
}
// Producer.java
BasicQueue queue = new BasicQueue("./", "task");
while (true) {
queue.enqueue(("task " + i++).getBytes("UTF-8"));
Thread.sleep(random.nextInt(1000));
}
// Consumer.java
BasicQueue queue = new BasicQueue("./", "task");
while (true) {
byte[] bytes = queue.dequeue();
if (bytes == null) { Thread.sleep(random.nextInt(1000)); continue; }
System.out.println("consume: " + new String(bytes, "UTF-8"));
}
两个独立 JVM 进程同时运行,通过共享 task.data/task.meta 实现零依赖协作。
force() 性能:频繁强制刷盘会严重影响吞吐,通常依赖 OS 延迟写回。内存映射文件是高性能 IO 与进程间通信的利器。通过 MappedByteBuffer,Java 开发者可轻松驾驭 OS 级零拷贝能力,构建持久化、跨进程的数据结构(如消息队列、索引、缓存)。理解其「按需分页」「共享内存」「最终一致性」特性,是构建高性能系统存储层的关键。