内存回收
之前提到当linux的内存不足时候会通过oom机制来kill程序加速释放内存,除了这种方法,linux还可以在资源不足时候释放可以回收的内存。哪些是可回收的内存呢?
- buffer/cache:这些被成为文件页(File-backed Page)大部分文件页可以直接回收,需要了在从磁盘读取,如果系统只修改了内存还没有写入到磁盘,我们成为脏页,需要写回磁盘在就进行回收
- 可以在应用程序中,通过系统调用 fsync ,把脏页同步到磁盘中;
- 也可以交给系统,由内核线程 pdflush 负责这些脏页的刷新;
- 应用程序动态分配的内存,我们称之为匿名页(Anonymous Page),会通过Swap换入/换出的方式将内存写入到磁盘中来释放内存。
Swap的机制介绍
swap将补偿访问的内存通过换入将内存数据写到磁盘中,需要了再从磁盘读取这实际上是一种用时间换空间的思想。
回收方式
- 直接回收:大部分动态分配的内存都可以直接回收
kswapd0:一个专门的内核线程用来定期回收内存。为了衡量内存的使用情况,kswapd0定义了三个内存阈值(watermark,也称为水位),分别是页最小阈值(page min),页最小阈值(pages_min)、页低阈值(pages_low)和页高阈值(pages_high)。剩余内存,则使用 pages_free 表示。这里,我画了一张图表示它们的关系。
kswapd0的运行机制如下:
小于pages_min:内存页耗尽
小于pages_low:内存页不足,kswapd0 会执行内存回收,直到剩余内存大于高阈值为止
小于pages_high:内存资源还可以满足申请的需求
大于pages_high:内存资源充足我们可以通过修改/proc/sys/vm/min_free_kbytes来修改pages_min,其他的pages_low和pages_high都是通过pages_min计算出来的。
NUMA 与 Swap
NUMBA是(Non-Uniform Memory Access),多个cpu会被划分到不通的node中,我们可以用numbactl看到如下的情况,只有一个node,对应了0,1俩个cpu,有7977MB的内存,空余内存是4416MB。
1 | $ numactl --hardware |
实际上,前面提到的三个内存阈值(页最小阈值、页低阈值和页高阈值),都可以通过内存域在 proc 文件系统中的接口 /proc/zoneinfo 来查看。比如,下面就是一个 /proc/zoneinfo 文件的内容示例:
1 |
|
内存资源紧张时候,默认采用回收匿名页数以及文件页数的方式来回收内存,可以通过/proc/sys/vm/zone_reclaim_mode调整
- 默认的 0 ,也就是刚刚提到的模式,表示既可以从其他 Node 寻找空闲内存,也可以从本地回收内存。
- 1、2、4 都表示只回收本地内存,2 表示可以回写脏数据回收内存,4 表示可以用 Swap 方式回收内存。
swappiness
内存回收有俩种方式
- 回收文件页:通过fsync将buffer和cache写入到磁盘来释放这一部分内存
回收匿名页:通过swap来释放匿名页内存
Linux 提供了一个 /proc/sys/vm/swappiness 选项,用来调整使用 Swap 的积极程度。范围是 0-100,数值越大,越积极使用 Swap,也就是更倾向于回收匿名页;数值越小,越消极使用 Swap,也就是更倾向于回收文件页。
注意swappiness只是一个趋势。
案例排查
首先确认swap开启,通过free确认,如果没开启可用下面方法开启
1 |
|
测试方案:用命令dd
1 | # 写入空设备,实际上只有磁盘的读请求 |
这时候我们用sar -r命令观察,我们发现kbmemfree值越来越少,%memused越来越大,且kbbuffers越来越大,因为dd直接和磁盘交互,符合预期。
1 | # 间隔1秒输出一组数据 |
- 刚开始,剩余内存(kbmemfree)不断减少,而缓冲区(kbbuffers)则不断增大,由此可知,剩余内存不断分配给了缓冲区。
- 一段时间后,剩余内存已经很小,而缓冲区占用了大部分内存。这时候,Swap 的使用开始逐渐增大,缓冲区和剩余内存则只在小范围内波动。
为什么缓冲区只在小范围内波动?由哪些进程导致,这时候我们用cachetop进行进一步排查发现是dd导致的,他的READ_HIT只有50%命中率且Miss的page已经到了41022页,可见也符合我们猜测的预期。
1 | $ cachetop 5 |
为什么Swap开始升高了?不是应该释放buffer么?我们查看/proc/zoneinfo返现,内存小于low,内存就开始执行回收才做恢复到high附近,周而复始。
1 |
|
我们发现有时候释放的是未知页即直接内存有时候是文件页即buffer,这是由于我们swappiness配置导致的
1 | cat /proc/sys/vm/swappiness |