首页/系统/Linux 内核与性能调优速查/MADV_DONTNEED / MADV_FREE 主动释放
内存与 OOM58 条

MADV_DONTNEED / MADV_FREE 主动释放

应用用 madvise 主动释放区间页:DONTNEED 立即丢弃(文件页可重读、匿名页数据丢失),FREE 延迟回收但在内存紧张前保留数据。

两种释放语义

MADV_DONTNEED 与 MADV_FREE 是应用主动归还内存的两种 madvise 行为。DONTNEED 立即丢弃指定区间的页映射:文件映射页会丢弃缓存副本、再次访问需要重新读盘;匿名页则意味着数据丢失(再次访问得到零页)。FREE 是延迟回收——先把页标记为可回收,内存紧张时才真正释放,在此之前数据仍可读到,适合应用还想保留这些数据的场景。

基本信息

接口madvise(2),作用于调用进程自己的映射
立即释放MADV_DONTNEED
延迟释放MADV_FREE(内核 4.5 起提供)
验证方式/proc/<pid>/smaps_rollup 的 Rss 变化
典型使用者glibc 的堆归还路径、部分缓存类应用的内存整理逻辑
副作用释放后再次访问会触发缺页,匿名页数据不可恢复

典型使用场景

  • 缓存类应用主动归还已不需要的缓冲区,比等内核回收更可控
  • 长时间运行的守护进程在空闲时段主动收缩内存占用
  • 批处理任务结束后释放临时大块内存,避免 RSS 长期虚高
  • 把容器内存水位控制在限额以下,降低触发 OOM 的风险
  • 验证应用是否真的在归还内存:调用前后对比 Rss 是否下降

验证与调用

验证与调用
grep -E '^(Rss|Anonymous)' /proc/12345/smaps_rollup  # 调用前后对比这两项
grep -E 'heap' /proc/12345/maps  # 确认堆区间地址范围,作为 madvise 的目标
# 应用侧调用示意(C):立即释放,之后访问会触发缺页
# madvise(addr, len, MADV_DONTNEED);
# madvise(addr, len, MADV_FREE);  // 4.5+,标记为可回收,压力时才真正丢弃
cat /sys/fs/cgroup/myapp.slice/memory.current  # 从容器视角确认用量是否下降
grep -E '^anon ' /sys/fs/cgroup/myapp.slice/memory.stat  # 匿名页是否真的减少
pidstat -r -p 12345 1 3  # 观察释放后缺页速率是否升高

两种调用对照

调用语义数据与代价
MADV_DONTNEED立即丢弃区间映射文件页需重新读盘;匿名页数据丢失
MADV_FREE标记可回收,压力时才释放释放前数据仍可读,代价是时机不可控
文件映射上的 DONTNEED丢弃缓存副本后续访问重新读盘,延迟可见
验证方式对比 smaps_rollup 的 RssVmSize 不变,只有物理页归还
DONTNEED 之后再次访问会触发缺页:文件映射页需要重新读盘,匿名页是零页。在延迟敏感的路径上使用前,要确认业务逻辑能承受这种代价,否则释放换来的内存节省会被缺页延迟抵消。
MADV_FREE 不会立刻降低 Rss,它只是把页标记为可回收;如果此后内存充裕,这些页可能长期保留。期待「调用后内存立刻下降」时应选 DONTNEED。

易错点

  • 对匿名页使用 DONTNEED 前未确认数据已落盘:造成实际数据丢失,且难以恢复
  • 以为 FREE 会立即释放内存:它只是标记,回收时机由内核在压力下决定
  • 在热路径频繁调用:缺页与相关开销会转化为延迟抖动
  • 把 Rss 下降等同于进程内存占用整体下降:虚拟地址空间大小并不改变
  • 两种语义混用而不做区分记录:行为表现不一致,后续排障无法归因

数据来源与参考

  • man-pages madvise(2)
  • Linux 内核 Documentation/admin-guide/mm/
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌