有些分配器是懒惰的吗?

时间:2009-05-14 16:38:47

标签: linux memory-management malloc

我在Linux上编写了一个C语言程序,它使用mallocs内存,在循环中运行它,并且TOP没有显示任何内存消耗。

然后我用内存做了一些事情,TOP确实显示了内存消耗。

当我使用malloc时,我真的“得到了内存”,还是有“懒惰”的内存管理,只有在我使用它时才会给我内存?

(还有一个选项,当我使用它时TOP才知道内存消耗,所以我不确定这个...)

由于

6 个答案:

答案 0 :(得分:17)

在Linux上,malloc使用sbrk()或mmap()请求内存 - 无论哪种方式,您的地址空间都会立即扩展,但Linux在第一次写入相关页面之前不会分配实际的物理内存页面。您可以在VIRT列中查看地址空间扩展,而在RES中查看实际的物理内存使用情况。

答案 1 :(得分:5)

这开始有点偏离主题(然后我将它与你的问题联系起来),但是发生的事情类似于在Linux中分叉进程时发生的情况。在分叉时,有一种称为写入时复制的机制,它只在写入内存时复制新进程的内存空间。这样,如果分叉进程立即执行一个新程序,那么你就节省了复制原始程序内存的开销。

回到你的问题,这个想法是类似的。正如其他人所指出的那样,请求内存会立即获得虚拟内存空间,但实际页面只会在写入时分配。

这是为了什么目的?它基本上使mallocing内存成为一个或多或少恒定的时间操作Big O(1)而不是Big O(n)操作(类似于linux调度程序扩展它的工作方式而不是在一个大块中执行它)。

为了证明我的意思,我做了以下实验:

rbarnes@rbarnes-desktop:~/test_code$ time ./bigmalloc 

real    0m0.005s
user    0m0.000s
sys 0m0.004s
rbarnes@rbarnes-desktop:~/test_code$ time ./deadbeef 

real    0m0.558s
user    0m0.000s
sys 0m0.492s
rbarnes@rbarnes-desktop:~/test_code$ time ./justwrites 

real    0m0.006s
user    0m0.000s
sys 0m0.008s

bigmalloc程序分配了2000万个整数,但对它们没有任何作用。 deadbeef在每个页面写入一个int,导致19531次写入,而justwrits分配19531个int并将它们归零。正如你所看到的,deadbeef的执行时间比bigmalloc长大约100倍,比justwrites长约50倍。

#include <stdlib.h>    

int main(int argc, char **argv) {

int *big = malloc(sizeof(int)*20000000); // allocate 80 million bytes

return 0;

}

#include <stdlib.h>    

int main(int argc, char **argv) {

int *big = malloc(sizeof(int)*20000000); // allocate 80 million bytes

// immediately write to each page to simulate all at once allocation

// assuming 4k page size on 32bit machine

for ( int* end = big + 20000000; big < end; big+=1024 ) *big = 0xDEADBEEF ;    

return 0;

}

#include <stdlib.h>

int main(int argc, char **argv) {

int *big = calloc(sizeof(int),19531); // number of writes

return 0;
}

答案 2 :(得分:3)

是的,除非您触摸它,否则内存不会映射到您的memoryspace中。 mallocing memory只会设置分页表,以便他们知道当你在分配的内存中获得页面故障时,应该映射内存。

答案 3 :(得分:1)

该功能称为 overcommit - 内核通过增加数据段大小来“承诺”您的内存,但不会为其分配物理内存。当您触摸该新空间中的地址时,进程页面将进入内核,然后内核尝试将物理页面映射到内核。

答案 4 :(得分:1)

是的,请注意VirtualAlloc标志,

MEM_RESERVE
MEM_COMMIT

嘿,但对于 Linux 或任何POSIX/BSD/SVR#系统,vfork()已存在很长时间并提供了类似的功能。

  

vfork()函数不同于   fork()只在那个子进程中   可以与。分享代码和数据   调用进程(父进程)。这个   加快克隆活动的速度   存在风险的完整性   父进程如果vfork()被滥用。

     

vfork()用于任何目的   除了作为直接的前奏   从exec调用函数   不建议家人或_exit(),

     

可以使用vfork()函数   没有完全创建新流程   复制旧的地址空间   处理。如果分叉过程很简单   打算调用exec,数据空间   从父项复制到子项   fork()未使用。这是   在分页中效率特别低   环境,制作vfork()   特别有用。依赖于   父数据空间的大小,   vfork()可以给出重要意义   fork()的性能提升。

答案 5 :(得分:0)

您使用的是编译器优化吗?也许优化器已经删除了分配,因为你没有使用分配的资源?