Linux内存泄漏测试demo仅VIRT增长、RES无上升原因咨询
核心原因:Linux的「按需分配物理页」机制
你观察到的现象是Linux内存管理的默认行为,相关知识点如下:
- 虚拟地址和物理内存是分离的,分配虚拟地址≠分配物理内存
你调用malloc/calloc时,libc和内核只给你的进程预留了一段连续的虚拟地址空间,体现在top的VIRT指标增长。此时内核根本没有给这段地址绑定任何物理内存页,只有当你首次读写这段地址的某一页时,才会触发缺页中断,内核才会真正分配物理页并绑定到对应的虚拟地址上,这时候RES(常驻内存)才会增长。 calloc的零页优化进一步推迟了物理页分配
因为calloc要求返回的内存全为0,Linux内核专门设置了一个全局的只读「零页」,所有刚通过calloc分配、还未被写入的内存,都会先映射到这个共享零页上。哪怕你读这段内存,内核也只会给你返回这个共享页的内容,不会分配新的物理页,只有你执行写入操作时,才会触发写时复制(COW)分配单独的物理页。- 你遇到的SIGSEGV原因
当你取消注释访问数组的代码后,每分配一段内存就会触发对应页的物理内存分配,当总分配量超过物理内存+交换分区的总容量时,内核无法再分配新的物理页,就会给进程发送SIGSEGV信号终止访问。
调整测试程序的方案
如果你要让内存分配真实占用物理内存,只需要在分配后给每一个内存页至少写入1个字节即可,示例修改如下:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main() { size_t mbs = 32; long page_size = sysconf(_SC_PAGESIZE); // 获取系统页大小,默认4096字节 char * leak; while (1) { leak = malloc(1024 * 1024 * mbs * sizeof(char)); if (!leak) { perror("malloc failed"); break; } // 遍历每个页写入1字节,触发物理内存分配 for (size_t i = 0; i < 1024 * 1024 * mbs; i += page_size) { leak[i] = 0; } printf("%9lu MiB allocated at address %p.", mbs, leak); printf(" Press enter to double them."); getchar(); mbs *= 2; } }
运行修改后的程序,你就能看到RES指标随着分配量同步增长了。
额外提示:limits.conf内存限制的注意事项
现代Linux内核已经废弃了limits.conf中rss项的物理内存限制功能,如果你要按用户维度限制实际物理内存使用,更稳妥的方案是通过cgroup实现。limits.conf中目前仍生效的内存相关限制是as,它限制的是进程的最大虚拟地址空间大小。
内容的提问来源于stack exchange,提问作者Patrizio Bertoni
相关产品推荐
相关产品推荐

