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

时间:2018-05-02 16:30:24

标签: c memory-leaks operating-system

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

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

以下是代码:

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

void main()
{
    while(1)
    {
        int *a;
        a = (int*)calloc(65536, 4);
    }
}

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

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

1 个答案:

答案 0 :(得分: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));
    }
}

如果你写入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);
    }
}

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

64位情况怎么样?

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

因此,在64位系统上,您可以分配~8 TiB,而32位系统可以分配~2 GiB,并且开销是导致问题的原因。每次调用malloccalloc时,通常会产生少量开销。

另见Why malloc+memset is slower than calloc?