欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

容器内存管理之内存回收

程序员文章站 2024-03-11 18:38:55
...

Linux操作系统的体系架构分为内核态和用户态,相应地,内存也有内核态内存和用户态内存,比如,内核需要为页表、内核栈、slab分配内存,用户态进程里需要分配堆内存、栈内存、共享库内存,以及文件读写的Page Cache。回到容器的内存管理,Memory Cgroup不会对内核内存做限制,只会对用户态内存做限制。与用户态相关的两种内存类型主要是RSS和Page Cache。

RSS是Resident Set Size的缩写,也就是进程申请到的物理页面的内存大小。对于进程来说,RSS内存包含了进程的代码段内存、栈内存、堆内存和共享库的内存,这些内存都是进程运行所必须的。通过malloc()/memset()得到的内存,就属于堆内存。

Page Cache是指Linux系统在进程对磁盘上的文件做读写操作后,为读写到的页面分配的内存。Page Cache主要提供缓存的作用,以提高磁盘文件的读写性能。

应用程序在使用malloc()申请内存的时候,系统只是把申请到的虚拟地址空间分配给了进程,但并没有把实际的物理内存页面分配给进程。只有当进程对这块内存地址真正做读写的时候,系统才会为其分配真正的物理内存。进程真正得到的物理内存,就是RSS。

例如,用malloc()申请100MB的内存,代码如下,

p = malloc(100 * MB);
if (p == NULL)
    return 0;

执行以上代码后,用top命令查看该进程的内存占用,结果如下,可以看到程序的虚拟空间VIRT已经有了106728KB,大约100MB,但实际的物理内存RES(Resident的缩写,即RSS)只有688KB。

容器内存管理之内存回收

接下来,我们往申请到的内存空间里,写入20MB数据:

memset(p, 0x00, 20 * MB)

再用top查看,可以看到虚拟地址空间还是106728,但物理内存RES已经变成了21432(约20MB)。

容器内存管理之内存回收

再来看看Page Cache在实际使用中的情况。下图是程序在读取100MB文件之前和之后的内存使用情况(单位是MB),读取文件之前,buff/cache为388MB,读取文件之后,buff/cache为506MB,多出来约100MB。

容器内存管理之内存回收

在Linux系统中,只要有空闲的内存,系统就会自动把读写过的磁盘文件页面写入到Page Cache中。那么当Page Cache占用了RSS之外的全部内存后,如果进程需要申请更多物理内存,会怎么样呢?这就涉及到了Linux系统的内存页面回收机制,它会根据系统里的空闲物理内存是否低于某个阈值,来决定是否启动内存回收。

由于Page Cache主要起到缓存的作用,因此内存回收时首先会释放Page Cache的内存。内存回收一般采用最近最少使用原则(LRU,Least Recently Used),决定哪些页面先被释放。一旦Page Cache占用的页面被释放,就可以满足进程再次申请物理内存的需求。

容器的内存回收机制与Linux系统的内存回收机制是一致的。Memory Cgroup控制组里的RSS内存和Page Cache内存的总和,正好就是memory.usage_in_bytes的值。当控制组里的进程需要申请新的物理内存时,如果memory.usage_in_bytes的值超过了控制组的内存上限memory.limit_in_bytes,这时内存回收就会被调用起来。内存回收会根据新申请的内存大小,释放一部分Page Cache内存,保证进程能成功申请到新的物理内存,并且整个控制组里总的物理内存开销memory.usage_in_bytes依然在上限值memory.limit_in_bytes以内。

由于memory.usage_in_bytes是RSS和Page Cache内存的总和,它不能反映容器真实内存使用量。要想得到真实的RSS使用量,我们可以通过查看memory.stat中的RSS值来实现:

容器内存管理之内存回收

 

主要参考:李程远《容器实战高手课》