一些分配器是懒惰的吗?

我在Linux下编写了一个C程序mallocs内存,循环运行它,TOP没有显示任何内存消耗。

那么我已经做了一些内存,TOP显示内存消耗。

当我malloc,我真的“获得记忆”,还是有一个“懒惰”的内存pipe理,只有当我使用它时,给我的内存?

(也有一个选项,TOP只知道内存消耗,当我使用它,所以我不知道这个..)

谢谢

在Linux上,malloc使用sbrk()或mmap()来请求内存,不管是哪种方式,你的地址空间都会立即被扩展,但是直到第一次写入页面时,Linux才会分配实际的物理内存页面。 您可以在VIRT列中看到地址空间扩展,而RES中的实际物理内存使用情况。

是的,内存不会映射到你的memoryspace,除非你触摸它。 mallocing内存将只设置分页表,以便他们知道何时在分配的内存中发生页面错误时,内存应该被映射到内存中。

这开始了一个小题目(然后我会把它与你的问题),但是发生什么类似于当你在Linux中分叉的过程发生的事情。 分叉时,有一种称为“拷贝写入”的机制,它只在内存写入时才复制新进程的内存空间。 这样,如果分叉的进程exec立即执行一个新的程序,那么你已经节省了复制原始程序内存的开销。

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

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

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

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写入一个整数到每个页面导致19531写入和justwrites分配19531整数和零。 正如你所看到的,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; } 

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

该功能被称为overcommit – 内核通过增加数据段的大小来“保证”你的内存,但是不会为它分配物理内存。 当你在这个新的空间中触摸一个地址时,进程页面会错误地进入内核,然后内核尝试将物理页面映射到内核。

是的,请注意VirtualAlloc标志,

 MEM_RESERVE MEM_COMMIT 

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

vfork()函数与fork()的不同之处在于,子进程可以与调用进程(父进程)共享代码和数据。 如果vfork()被滥用,这会显着加速克隆活动对父进程完整性的风险。

不建议将vfork()用于任何目的,除非作为从exec系列立即调用函数的前奏或者_exit()。

vfork()函数可以用来创建新的进程而不需要完全复制旧进程的地址空间。 如果分叉进程只是调用exec,则不会使用由fork()从父进程复制到子进程的数据空间。 这在分页环境中效率特别低,使vfork()特别有用。 根据父数据空间的大小,vfork()可以比fork()提供显着的性能改进。