两种释放语义
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 的 Rss | VmSize 不变,只有物理页归还 |
DONTNEED 之后再次访问会触发缺页:文件映射页需要重新读盘,匿名页是零页。在延迟敏感的路径上使用前,要确认业务逻辑能承受这种代价,否则释放换来的内存节省会被缺页延迟抵消。
MADV_FREE 不会立刻降低 Rss,它只是把页标记为可回收;如果此后内存充裕,这些页可能长期保留。期待「调用后内存立刻下降」时应选 DONTNEED。