页缓存为何不释放 内存占用居高不下的排查思路

页缓存为何不释放 内存占用居高不下的排查思路

1024KB1024KB 软件测试与质量 2026-10-25 20:20:12 浏览 99939 字数 2440

Linux文件读写性能的核心依赖之一是页缓存(page cache),它的工作机制决定了它不会因为你没有读写文件而主动释放内存。页缓存是内核为文件内容保留的内存副本,而脏页需要后台线程异步写回磁盘,只有当整机内存出现回收压力或脏页比例超过阈值时,内核才会触发回收行为。因此,看到页缓存占用大量内存并不意味着内存泄漏,而是一个需要结合回收机制与内核参数来判断的系统状态。

页缓存的核心作用与回收前置条件

页缓存不是内存泄漏,而是内核设计用来减少磁盘I/O的一种缓存策略。读取一个文件时,内核先把磁盘块的内容读入页缓存,后续再读同一个文件,就直接从内存返回数据;写入文件时,数据先落在页缓存中,标记为脏页,由内核的pdflush线程(或writeback机制)统一写回磁盘。

根据内核文档《Memory Management》中的描述,页缓存以基数树(radix tree)结构索引文件页,每个页的生命周期由reclaim逻辑管理。回收页缓存的前提有两个:第一,系统空余内存低于某一水位线,kswapd内核线程开始换页回收;第二,脏页数量超过vm.dirty_ratio或vm.dirty_background_ratio设置的阈值。你观察到的页缓存长时间不释放,往往是这两个条件都未被触发。

一个常见的误解是系统内存越接近上限,内核就越积极清空页缓存。事实上,如果没有新的内存请求导致分配失败,kswapd不会主动扫描缓存的inactive链。也就是说,只要没有内存压力,内核宁可把本该用于业务的内存空间留给页缓存,也不去主动做无用功。

页缓存回收机制的触发路径

Linux内核只在两种场景下手动处理页缓存:内存分配压力或显式触发drop_caches操作。前者由分配器向伙伴系统请求内存,发现空闲块不足时调用shrink_slab和shrink_page_list,逐出一部分干净页;后者是你主动执行sync; echo 1 > /proc/sys/vm/drop_caches,把干净页释放回伙伴系统。

在排查内存占用时,先使用free命令查看各字段的分布。free输出的cached列包含tmpfs和shmem文件系统的页,以及普通的文件页缓存;buffers则是块设备上的缓冲数据。将cached与available做对比,如果available明显小于cached,说明当前页缓存的实际可回收比例并不高。

free -m
total        used        free      shared  buff/cache   available
Mem:          15872       12063         301        1024        3508         2704

从上面的输出可见,cached有3508MB,但available只有2704MB,中间的差值包含了不可回收的共享内存页和内核栈等。此时页缓存并未失控,而是可回收比例已被其他不可回收页压缩。

脏页回写阈值与业务写入模式的关系

脏页含量直接决定了页缓存回收后是否需要写磁盘,而这与你的写入模式强相关。内核参数vm.dirty_background_ratio默认值为10,含义是当脏页占全部可用内存的10%时,后台内核线程开始回写脏页,但不阻塞业务。vm.dirty_ratio默认值为20,当脏页占比达到这个值时,触发同步写回,写文件的进程会被阻塞直到脏页落盘。

如果你的服务频繁追加写入大文件,比如日志系统或消息队列,脏页比例会稳定在background_ratio附近,此时观察到页缓存占用高且writeback持续有I/O活动,是预期行为。反过来,如果读多写少,页缓存大部分是干净页,这些页面几乎不会引发回收成本,页缓存保留对系统有利。

关键的是,不要看到页缓存高就立刻执行echo 3 > drop_caches。干净页被逐出后,虽然free内存增加,但下次访问文件又得全部重新读盘,性能反而变差。更合理的做法是分析内存压力与磁盘I/O负载的关联,再决定是否调整dirty参数。

内核参数默认值作用触发方式
vm.dirty_ratio20%脏页占内存上限,到达后同步阻塞写进程写路径
vm.dirty_background_ratio10%启动后台回写线程内核线程
vm.dirty_writeback_centisecs500定期唤醒回写线程的间隔(百分之一秒)定时器
vm.dirty_expire_centisecs3000脏页超过该时限才强制写回定时器

页缓存与数据库预读行为叠加造成的误判

数据库服务如MySQL或PostgreSQL会调用posix_fadvise或madvise技术管理自身缓存,但它们的预读行为会让页缓存出现短期猛增。InnoDB每次从磁盘读页时会触发一次长度为64KB左右的预读,如果表扫描操作频繁,页缓存会在短时间内吸纳大量文件页,这属于数据库的常规路径。

这类场景下正确区分页缓存和数据库缓冲池很重要。InnoDB缓冲池是进程内存,不属于页缓存;页缓存是内核层的文件副本。当表格数据远小于缓冲池时,页缓存中只是出现了数据页的镜像,这种镜像不会导致额外内存泄漏,因为InnoDB通常使用O_DIRECT绕过页缓存直接读写数据文件。只有在未使用O_DIRECT的模式下,页缓存才会承载真正的数据库I/O流量。

在排查页缓存占用前,先确认你的数据库是否启用了O_DIRECT。使用如下命令检查MySQL的innodb_flush_method参数:

mysql -e "show variables like 'innodb_flush_method';"
+---------------------+----------------+
| Variable_name       | Value          |
+---------------------+----------------+
| innodb_flush_method | O_DIRECT       |
+---------------------+----------------+

如果结果不是O_DIRECT,那么数据库读写会同时产生页缓存和缓冲池双份副本,这是内存占用翻倍的重要原因。

如何准确判断页缓存是否需要人为清理

判断页缓存是否需要处理的依据是内存压力指标,而不是缓存绝对值。查看/proc/pressure/memory文件中的PSI数据,其中some和full字段显示任务因内存分配而受阻的时间占比。如果full值长期为0,说明没有任务因为内存而等待,页缓存保留对性能有益,不需要干预。

cat /proc/pressure/memory
some avg10=0.00 avg60=0.00 avg300=0.00 total=0
full avg10=0.00 avg60=0.00 avg300=0.00 total=0

当PSI的平均值持续高于5%时,才需要考虑以下几种调整策略。第一种,降低脏页阈值,让后台写回更早介入,避免脏页堆积到导致同步阻塞。第二种,为热数据文件开启O_DIRECT绕过页缓存,让文件直接读取磁盘。第三种,调整swappiness优先回收页缓存而非匿名页。

常见参数的调整方式与适用场景

对于写多读少的服务,将vm.dirty_background_ratio调低至5,dirty_ratio调低至10,能让后台回写线程更早启动,减少writeback积累。调整命令如下:

sysctl -w vm.dirty_background_ratio=5
sysctl -w vm.dirty_ratio=10

这些改动对海量顺序写日志的应用帮助明显。对于读多写少的Web服务,保留默认值即可,因为干净页的回收成本远低于再次读盘的开销,人为释放页缓存只会伤害缓存命中率。

另一个被忽视的维度和swap设置有关。vm.swappiness值控制在回收时优先选择匿名页还是文件页的权重,默认值60会让内核在内存不足时同时回收文件页和匿名页。如果页缓存占比高且可用内存低,将swappiness调低至10以下,强制减少匿名页的交换,从而保留更多文件缓存。

页缓存占用的健康区间与监控建议

页缓存占用没有统一的标准数值,而是和业务读写比例强相关。根据网上公开的运营经验以及kernel社区的讨论,页缓存占可用内存的20%到30%属于常见的默认区间,只读缓存类服务占50%以上也是常态。这里提供一个更客观的判断方法:监控page reclaims指标和major faults的数量,如果major faults持续上升但页缓存保持高位,说明冷文件访问在频繁把新页拉入缓存,但缓存命中率并没有提升,此时考虑缓存冷热分区。

在监控层面,建议采集以下四个指标来跟踪页缓存状态:cached、dirty、writeback和pgscan。cached值反映缓存总量,dirty反映待写回的数据量,writeback反映正在写回的量,pgscan则表明页缓存回收是否频繁发生。当dirty接近dirty_ratio阈值且pgscan同步升高时,系统已经到了内存瓶颈的边缘,需要调整业务策略或扩容内存。

最后提醒一个误区:页缓存是Linux内核优化I/O的核心机制,现代内核的回收算法已经足够成熟,不会因缓存不释放而引发系统崩溃。真正需要关注的,是缓存回收时引发的延迟抖动和写阻塞是否影响在线业务,而不是free命令下面那行遥不可及的free数字。

分享到:

评论

加载中...
我
还没有评论,快来抢沙发吧~