不能在32位Linux中耗尽物理内存

roo*_*oot 1 c operating-system memory-leaks

所以我有一个有趣的基于操作系统的问题.我花了最后几个小时与我认识的任何有C编程经验的人交谈,似乎没有人能够就这种行为发生的原因找到明确的答案.

我有一个故意设计用于导致极端内存泄漏的程序(作为分配后不释放内存时会发生什么的一个例子).在64位操作系统(Windows,Linux等)上,它可以做它应该做的事情.它填充物理内存,然后填充操作系统的交换空间.在Linux中,该过程随后由OS终止.然而,在Windows中,它不是,并且它继续运行.最终结果是系统崩溃.

这是代码:

#include <stdlib.h>
#include <stdio.h>

void main()
{
    while(1)
    {
        int *a;
        a = (int*)calloc(65536, 4);
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,如果您在32位Linux发行版上编译并运行此代码,则它根本不会影响物理内存使用.它占用了我4 GB分配RAM的大约1%,之后它永远不会上升.我没有32位Windows的合法副本进行测试,所以我不能确定这也发生在32位Windows上.

有人可以解释为什么使用calloc将填充64位Linux操作系统的物理内存,而不是32位Linux操作系统?

Die*_*Epp 5

malloccalloc功能在技术上不分配内存,尽管他们的名字.它们实际上使用操作系统级别的读/写权限分配程序的部分地址空间.这是一个微妙的差异,大多数时候都不相关.

如编写的那样,该程序仅占用地址空间.最终,calloc将开始返回NULL但程序将继续运行.

#include <stdlib.h>
// Note main should be int.
int main() {
    while (1) {
        // Note calloc should not be cast.
        int *a = calloc(65536, sizeof(int));
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你写入calloc返回的地址,它将强制内核分配内存来支持这些地址.

#include <stdlib.h>
#include <string.h>
int main() {
    size_t size = 65536 * 4;
    while (1) {
        // Allocates address space.
        void *p = calloc(size, 1);
        // Forces the address space to have allocated memory behind it.
        memset(p, 0, size);
    }
}
Run Code Online (Sandbox Code Playgroud)

写入返回的块中的单个位置是不够的,calloc因为分配实际内存的粒度是4 KiB(页面大小... 4 KiB是最常见的).因此,只需写入每个页面即可.

那64位的情况怎么样?

分配地址空间有一些簿记开销.在64位系统上,您可以获得40或48位的地址空间,其中大约一半可以分配给程序,至少达到8 TiB.在32位系统上,这大约为2 GiB(取决于内核配置).

因此,在64位系统上,您可以分配~8 TiB,而32位系统可以分配~2 GiB,并且开销是导致问题的原因.每次调用malloc或通常都会有少量开销calloc.

另请参见为什么malloc + memset比calloc慢?