本文试图用最浅显的语言说明以下问题:
1、free 命令中的buffer/cache 是什么意思?
2、内存回收的机制是什么?
3、内存回收的门限是什么?也就是什么时候进行回收?
4、如何手动清除cache?
1、free 命令中的buffer/cache 是什么意思?
当我们的系统长时间运行一段时间后,执行 free 命令,会发现 buff/cache 占用的内存非常大,这里的 buffer/cache 到底是什么呢?又为什么会变得这么大呢?被它占用内存还能被其它进程正常使用吗?其实buff/cache 是指buffer cache 和 page cache,这里对这两个概念解释一下:
Page Cache
page cache 以page为单位,缓存文件内容。缓存在page cache中的文件数据,能够更快的被用户读取。同时对于带buffer的写入操作,数据在写入到page cache中即可立即返回,而不需等待数据被实际写到到磁盘,进而提高了上层应用读写文件的整体性能。
Buffer Cache
磁盘的最小数据单位为sector,每次读写磁盘都是以sector为单位对磁盘进行操作。
sector大小跟具体的磁盘类型有关,有的为512Byte, 有的为4K Bytes。无论用户是希望读取1个byte,还是10个byte,最终访问磁盘时,都必须以sector为单位读取,如果裸读磁盘,那意味着数据读取的效率会非常低。
同样,如果用户希望向磁盘某个位置写入(更新)1个byte的数据,他也必须整个刷新一个sector,言下之意,则是在写入这1个byte之前,我们需要先将该1byte所在的磁盘sector数据全部读出来,在内存中,修改对应的这1个byte数据,然后再将整个修改后的sector数据,一口气写入磁盘。
为了降低这类低效访问,尽可能的提升磁盘访问性能,内核会在磁盘sector上构建一层缓存,他以sector的整数倍力度单位(block),缓存部分sector数据在内存中,当有数据读取请求时,他能够直接从内存中将对应数据读出。当有数据写入时,他可以直接再内存中直接更新指定部分的数据,然后再通过异步方式,把更新后的数据写回到对应磁盘的sector中。这层缓存则是块缓存Buffer Cache。
从上面解释可以看出:Page Cache和Buffer Cache是一个事物的两种表现:对于一个Page而言,对上,它是某个File的一个Page Cache,而对下,它同样是一个磁盘Device上的一组Buffer Cache
这两种cache 在最早的内核中,是独立存在的,但在Linux-2.4以后,这两种cache已经融合到一起了,融合后的Buffer Cache不再以独立的形式存在,Buffer Cache的内容,直接存在于Page Cache中。关于buffer cache 和 page cache的发展进程,感兴趣的可以参考文章 [1]。
既然我们理解的这个概念,在linux系统中,只要我们打开过的文件,文件的内容和元数据信息都会存在 page cache中,随着打开的文件越来越多,page cache 的占用就会越来越大,这也就是为什么我们看到的 buff/cache 会非常大的原因,其实这是正常的,这是linux内核为了提高访问磁盘的性能而做的一种优化。
那被buff/cache占用的内存还可以被其它进程使用吗?答案是不可以的。随着buff/cache占用的内存越来越大,等大到一定的程度,内核会触发内存回收的线程来回收这些page,被回收后的page才可以被其它进程使用。
2、内存回收的对象、回收机制和门限
2.1 内存回收的对象
内存回收是linux内核发现内存已经不够用的时候去出动回收内存的一种机制,那回收的对象是谁呢?可能是 file-backed pages , 也可能是 swap 的内存 ,到底先回收哪一个呢,天平应该往那一边倾斜呢,这个在linux 里面是通过 swapiness这个参数来设置的。swapiness 是内存回收的其中1个参数 : 反应是否积极的使用swap 空间。如果回收的对象是 file-backed pages,就是 将内存中的page cache 都丢弃了,下次再用的时候,从disk中再都回来。
默认值:
$ cat /proc/sys/vm/swappiness
60
内存不足:free and file-backed pages < high water mark in a zone
swappiness=0 表示仅在内存不足的情况下,使用swap空间
swappiness=60 默认值
swappiness=100 内核将积极的使用swap空间
2.2 内存回收的机制
内存回收的机制可以下面一张图来理解:
内核给内存设置了三个水位:
- min_free_kbytes
- low_free_kbytes
- high_free_kbytes
如果此时前台有个进程APP,在疯狂的申请内存,每次申请2G,然后写内存,如上图所示,此时系统内存会逐渐减少,当达到如下水位:
high_free_kbytes 该水位是回收的目标,是保证空闲内存在 high水位以上,空闲内存达到high 水位以后,kswapd就会 stop relcaim。
low_free_kbytes 当系统内存减少到该水位,kswapd启动 reclaim,但是如果前台APP还是在疯狂的申请内存,而kswapd回收的速度比较慢,系统的空闲内存会继续减少,可能达到 min水位。
min_free_kbytes 系统剩余的最小可用的内存,当系统内存减少到该水位,还有进程线程在疯狂的要内存,如果系统回收的速度达不到不索取内存的速度,系统会直接阻塞你的前台进程或者线程,这个时候你会发现程序执行的很慢,这个过程在Linux 中的属于叫 direct reclaim
min_free_kbytes 就是系统无论如何都要保证的可用内存,那是不是所以的线程都无法使用该水位一下的内存呢?答案是否定的,
因为 Linux 里面是有一些情况是要使用 紧急内存的,比如 回收内存的代码,它也是需要内存的啊,它在申请内存的时候,会加上一个 PF_MEMALLOC 的标志,
带上这个标志之后,就可以不受 min 水位的限制,使用min水位一下的内存。
2.3 内存水位计算
本文基于linux4.19内核来说明
min_free_kbytes 的默认值:
min_free_kbytes 会随着内存的增大而增大,但不是线性的增大,根据代码mm/page_alloc.c里的说明:
这里的lowmem_kbytes 根据代码里是系统各个内存zone受管理的page之和,也就是系统内存的大小,在我的平台上 NXP-LS1046A ARM64平台上,16GB的内存大小:
那根据代码里的说明,我系统的 min_free_kbytes 大概在 16149Kbytes 左右,但是我系统里的值:
root@dddd:~# cat /proc/sys/vm/min_free_kbytes
45056
而且系统里没有做其它设置
看了下代码才确定 内核使能了 CONFIG_TRANSPARENT_HUGEPAGE
$ cat mm/Makefile | grep hugepa
obj-$(CONFIG_TRANSPARENT_HUGEPAGE) += huge_memory.o khugepaged.o
经过确认是 mm/hugepaged.c 中 set_recommended_min_free_kbytes进行的设置,具体设置的根据核方法,可以参考该函数,这里暂不做具体讲解。
watermark[min] = per_zone_min_free_pages
watermark[high] - watermark[low]
= watermark[low] - watermark[min]
= per_zone_min_free_pages * 1/4
3 如何手动清除cache?
参考 Documentation/sysctl/vm.txt
drop_caches
Writing to this will cause the kernel to drop clean caches, as well as
reclaimable slab objects like dentries and inodes. Once dropped, their
memory becomes free.
To free pagecache:
echo 1 > /proc/sys/vm/drop_caches
To free reclaimable slab objects (includes dentries and inodes):
echo 2 > /proc/sys/vm/drop_caches
To free slab objects and pagecache:
echo 3 > /proc/sys/vm/drop_caches
Refer:
[1]. buffer cache与page cache