我在Linux上编写了一个C语言程序,它使用mallocs内存,在循环中运行它,并且TOP没有显示任何内存消耗。
然后我用内存做了一些事情,TOP确实显示了内存消耗。当我使用malloc时,我真的“得到了内存”,还是有“懒惰”的内存管理,只有在我使用它时才会给我内存?
(还有一个选项,当我使用它时TOP才知道内存消耗,所以我不确定这个...)
由于
答案 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)
您使用的是编译器优化吗?也许优化器已经删除了分配,因为你没有使用分配的资源?