malloc 能申请到多少内存?(二)
为什么有个续集
本来是准备写关于 OOM-killer 和 cgroup 的一篇文章,然而在准备过程中对【malloc 能申请到多少内存?(一)】的测量程序重新做验证时,发现了点有意思的东西,感觉还可以当作一道面试题 :p。
重新用 malloc 检测可用内存
上一篇博文中我通过不断申请 size=1024B 的块来测量内存,最终结果是进程被 SIGKILL。为了让程序效率高一点,我对它进行了如下改进:
1 | // test1.c |
一运行我就傻眼了,好家伙:
1 | 131061 GB allocated |
难道是我代码写错了?那我再换个粒度进行测量:
1 | // test2.c |
结果呢,熟悉的 SIGKILL 又出现了。
(愚笨的我此刻并没有意识到问题出在哪里),于是打开 htop 再次分别运行两个程序。结果发现 test1.c 运行时间较短,CPU 负载适中,Mem 部分增长, Swp 压根没动;test2.c 运行时间较长,各项系统资源全面爆炸。
那么问题的原因就显而易见了:test1.c 中 malloc 虚假地分配了内存,而 test2.c 中 malloc 真实地分配了内存。
进一步验证分析
这里还要提一下 malloc 大致的工作原理:在初始化时,它会通过系统调用向操作系统申请一块内存,并用这块内存维护一个内存池。当遇到程序调用 malloc 申请小块内存时, malloc 会直接从内存池中找到最匹配的块交给调用者,避免了因系统调用导致的额外开销。而遇到程序调用 malloc 申请大块内存时,内存池中的存货显然不足以满足要求了,这时 malloc 就会再次通过系统调用(brk 或者 mmap,可以参考这篇文章)向操作系统申请内存。
什么叫虚假分配呢?就是只喊口号不干活。谁不干活呢?系统调用不干活。为什么不干活呢?懒。 怕你占着茅坑不拉屎。当你真正要用这块申请来的内存的时候,我再真正分配给你。为了进一步验证这一点,我们对 test1.c 进行一点修改:
1 | // test3.c |
再次运行,果真出现了 SIGKILL。
留一个小问题:为什么 test1.c 会申请得到一百多 TB 的内存呢?