面试知识库
中 困难

mmap原理与应用#

一句话答案#

mmap 将文件映射到进程虚拟地址空间,读写内存即读写文件,减少一次用户/内核态数据拷贝,RocketMQ 使用。

核心要点

原理: 建立虚拟地址 → 文件页的映射,访问时触发缺页加载

应用: RocketMQ(CommitLog 等存储文件) / 早期 MongoDB MMAPv1 引擎(3.2 起默认改为 WiredTiger,4.2 已移除 MMAPv1)/ 共享内存(进程通信)

vs read/write: mmap 少一次拷贝,适合大文件随机读写

面试回答(2分钟版)

mmap是一种将文件直接映射到进程虚拟地址空间的技术,映射之后读写内存就等于读写文件,省掉了传统read/write系统调用中从内核缓冲区到用户缓冲区的那一次数据拷贝。具体原理是mmap调用时只建立虚拟地址到文件页的映射关系,并不立即加载数据,等实际访问时触发缺页中断再按需从磁盘加载到Page Cache,进程通过页表直接访问Page Cache中的数据。它的优势在于减少了一次内存拷贝,而且多个进程可以映射同一个文件实现共享内存通信。RocketMQ用mmap来做消息存储,CommitLog是所有消息顺序追加写、消费时按ConsumeQueue里的偏移去读,mmap让写入变成内存写、省掉每条消息的read/write系统调用;Java的MappedByteBuffer单次最多映射2GB,所以CommitLog按1GB切分。MongoDB早期的MMAPv1引擎也基于mmap,但4.2起已移除,现在默认的WiredTiger不靠mmap。但mmap并不是万能的,对于小文件或者顺序读写场景,mmap建立映射的开销和缺页中断的代价反而不划算,普通read/write可能更合适。另外mmap会占用进程的虚拟地址空间,32位系统上可用空间有限。

追问与易错

追问方向:

  • “mmap 和 read 区别?”→ read 需要从内核缓冲区拷贝到用户缓冲区(多一次 CPU 拷贝),mmap 直接将内核 PageCache 映射到用户空间省掉这次拷贝
  • “适合什么场景?”→ 大文件随机读写(如数据库存储文件)、进程间共享内存通信、需要频繁读写同一文件区域的场景;小文件或纯顺序读场景 read 更合适
  • “RocketMQ 为什么用 mmap?”→ CommitLog 顺序追加写、消费时按偏移读取,大量小消息用 mmap 直接当内存读写,省系统调用和一次拷贝;Java MappedByteBuffer 单次映射上限 2GB,所以文件固定 1GB 一个,并提前预热(逐页写 0)避免写入时大量缺页

易错点:

  • ❌ mmap 比 read 永远快——小文件不一定
  • ❌ mmap 不占内存——占虚拟地址空间,访问过的页还会占物理内存(就是 PageCache,计入进程 RSS 的共享部分)