You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

mmap映射后内存占用翻倍咨询:为何进程与页缓存均占内存?

关于mmap机制内存占用翻倍的问题

问题描述

学习mmap机制时,理解是mmap将虚拟地址映射到页缓存,无需拷贝数据,系统中仅存一份数据副本。但调用mmap后再用read读取数据,发现内存占用增长为读取文件大小的两倍,疑问是理解有误还是代码问题。

测试前内存占用

$ free -m
               total        used        free      shared  buff/cache   available
Mem:            3924        1391        2280          13         251        2292
Swap:              0           0           0

测试代码

import mmap
import os
import time

# file2.db 是一个2GB的文件
with open("/var/tmp/file2.db", "r") as f:
    with mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ) as mm:
        x = mm.read(500000000)
        time.sleep(10000)

执行命令:

$ python3 mmap_read.py &

测试后内存占用

$ free -m
               total        used        free      shared  buff/cache   available
Mem:            3924        1703        1575          13         644        1980
Swap:              0           0           0

perf分析结果

$ sudo perf record python3 mmap_read.py & # 记录系统调用
$ sudo perf report

输出:

Samples: 128  of event 'cpu-clock:pppH', Event count (approx.): 1292929280
Overhead  Command  Shared Object      Symbol
  24.22%  python3  [kernel.kallsyms]  [k] do_user_addr_fault
   4.69%  python3  [kernel.kallsyms]  [k] rmqueue
   3.91%  python3  [kernel.kallsyms]  [k] __add_to_page_cache_locked
   3.91%  python3  [kernel.kallsyms]  [k] charge_memcg
   3.91%  python3  libc.so.6          [.] 0x00000000001a0e81
   3.12%  python3  [kernel.kallsyms]  [k] __lock_text_start
   3.12%  python3  [kernel.kallsyms]  [k] xas_load
   3.12%  python3  libc.so.6          [.] 0x00000000001a0ef0
   2.34%  python3  [kernel.kallsyms]  [k] __mod_lruvec_state
   2.34%  python3  [kernel.kallsyms]  [k] do_anonymous_page
   2.34%  python3  [kernel.kallsyms]  [k] free_unref_page_list
   2.34%  python3  [kernel.kallsyms]  [k] release_pages
   2.34%  python3  libc.so.6          [.] 0x00000000001a0e6f
   1.56%  python3  [kernel.kallsyms]  [k] __cgroup_throttle_swaprate
   1.56%  python3  [kernel.kallsyms]  [k] __mod_node_page_state
   1.56%  python3  [kernel.kallsyms]  [k] filemap_map_pages
   1.56%  python3  [kernel.kallsyms]  [k] pmd_page_vaddr
   1.56%  python3  [kernel.kallsyms]  [k] pmd_pfn
   1.56%  python3  [kernel.kallsyms]  [k] xa_get_order
   1.56%  python3  libc.so.6          [.] 0x00000000001a0e4c
   1.56%  python3  libc.so.6          [.] 0x00000000001a0e7d
   1.56%  python3  libc.so.6          [.] 0x00000000001a0e86
   1.56%  python3  libc.so.6          [.] 0x00000000001a0f47
   0.78%  python3  [kernel.kallsyms]  [k] __bio_add_page
   0.78%  python3  [kernel.kallsyms]  [k] __handle_mm_fault
   0.78%  python3  [kernel.kallsyms]  [k] __mem_cgroup_charge
   0.78%  python3  [kernel.kallsyms]  [k] __page_set_anon_rmap
   0.78%  python3  [kernel.kallsyms]  [k] arch_local_irq_enable
   0.78%  python3  [kernel.kallsyms]  [k] blk_mq_dispatch_rq_list
   0.78%  python3  [kernel.kallsyms]  [k] cgroup_rstat_updated
   0.78%  python3  [kernel.kallsyms]  [k] clear_page_erms
   0.78%  python3  [kernel.kallsyms]  [k] do_set_pte
   0.78%  python3  [kernel.kallsyms]  [k] elv_rqhash_add
   0.78%  python3  [kernel.kallsyms]  [k] finish_task_switch.isra.0
   0.78%  python3  [kernel.kallsyms]  [k] get_mem_cgroup_from_mm
   0.78%  python3  [kernel.kallsyms]  [k] handle_mm_fault
   0.78%  python3  [kernel.kallsyms]  [k] handle_pte_fault
   0.78%  python3  [kernel.kallsyms]  [k] kthread_blkcg
   0.78%  python3  [kernel.kallsyms]  [k] page_counter_try_charge
   0.78%  python3  [kernel.kallsyms]  [k] pmd_val
   0.78%  python3  [kernel.kallsyms]  [k] try_charge_memcg
   0.78%  python3  [kernel.kallsyms]  [k] xas_find
   0.78%  python3  [kernel.kallsyms]  [k] zap_pte_range
   0.78%  python3  libc.so.6          [.] 0x00000000001a0e5a
   0.78%  python3  libc.so.6          [.] 0x00000000001a0e76
   0.78%  python3  libc.so.6          [.] 0x00000000001a0f02
   0.78%  python3  libc.so.6          [.] 0x00000000001a0f07
   0.78%  python3  libc.so.6          [.] 0x00000000001a0f27
   0.78%  python3  libc.so.6          [.] 0x00000000001a0f57
   0.78%  python3  libc.so.6          [.] 0x00000000001a0f5f
   0.78%  python3  python3.10         [.] 0x000000000012161a
   0.78%  python3  python3.10         [.] 0x000000000012d084

疑问

原本预期buff/cache增长而used内存保持不变,因为进程应直接引用页缓存中的数据,请问这是什么原因?


解答

问题出在你调用了mm.read(500000000)这个操作。虽然mmap确实把文件映射到了进程虚拟地址空间,和页缓存共享同一份物理内存,但Python的mmap.mmap对象的read()方法并不是直接返回映射区域的引用,而是创建了一个新的字节对象,并把映射区域的数据拷贝到这个新对象里。

这就导致系统中存在两份数据:

  1. 页缓存中的文件数据(对应buff/cache的增长);
  2. Python变量x指向的字节对象(对应used内存的增长)。

数据验证

从你的free输出可以看到:

  • buff/cache从251MB增长到644MB,增加了约393MB;
  • used从1391MB增长到1703MB,增加了约312MB;
    两者加起来接近你读取的500MB(因为内存统计按页对齐,还有系统其他内存变动,所以数值不是精确的500MB)。

正确用法

如果想避免拷贝,直接使用mmap对象访问数据即可,比如通过切片或者索引:

import mmap
import time

with open("/var/tmp/file2.db", "r") as f:
    with mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ) as mm:
        # 直接访问映射区域,不产生拷贝
        first_byte = mm[0]
        # 或者获取视图,也不会拷贝
        data_view = mm[:500000000]
        time.sleep(10000)

这样used内存不会明显增长,只有buff/cache会因为文件页被加载而增加,符合你最初对mmap的预期。


内容的提问来源于stack exchange,提问作者Belive

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 14:05:23