在 c++ 中定义全局或静态数组时,它的内存不会在程序开始时立即保留,而只会在我们写入数组时保留。我发现令人惊讶的是,如果我们只写入数组的一小部分,它仍然不会保留整个内存。考虑下面这个稀疏写入全局数组的小例子:
#include <cstdio>
#include <cstdlib>
#define MAX_SIZE 250000000
double global[MAX_SIZE];
int main(int argc, char** argv) {
if(argc<2) {
printf("usage: %s <step size>\n", argv[0]);
exit(EXIT_FAILURE);
}
size_t step_size=atoi(argv[1]);
for(size_t i=0; i<MAX_SIZE; i+=step_size) {
global[i]=(double) i;
}
printf("finished\n"); getchar();
return EXIT_SUCCESS;
}
现在针对不同的步长执行此操作并查看 top 的输出,例如:
./a.out 1000000
./a.out 100000
./a.out 10000
./a.out 1000
./a.out 100
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
15718 user 20 0 1918m 1868 728 S 0 0.0 0:00.00 a.out
15748 user 20 0 1918m 10m 728 S 0 0.1 0:00.00 a.out
15749 user 20 0 1918m 98m 728 S 1 0.8 0:00.04 a.out
15750 user 20 0 1918m 977m 728 S 0 8.1 0:00.39 a.out
15751 user 20 0 1918m 1.9g 728 S 23 15.9 0:00.80 a.out
RES 列表示内存只保留在小块中,这也意味着阵列在物理内存中不太可能是连续的。有人对较低层次的事物有更多的了解吗?
这也有负面影响,我可以轻松运行许多程序,其中所有 VIRT 的总和超过物理内存,只要 RES 的总和低于。但是,一旦它们都写入全局数组,系统就会耗尽物理内存,并且程序会被发送 sigkill 或其他东西。
理想情况下,我想告诉编译器在开始时保留全局和静态变量的内存。可能的?
编辑
@Magnus:这些行实际上是正确的顺序。:) 以第一行为例,./a.out 1000000
意味着我在数组中每 100 万个条目写入一次,因此总共只有 250 个。这对应于只有 1868k 的 RES。在最后一个示例中./a.out 100
,每写入数百个条目,然后总内存也被物理分配 RES=VIRT=1.9g。从这些数字看来,每当一个条目被写入数组时,物理内存上都会保留一个完整的 4k 块。
@Nawaz:该数组在虚拟地址空间中是连续的,但据我了解,操作系统可能很懒惰,仅在实际需要时才保留物理内存。由于这是在小块中完成的,而不是一次完成整个数组,如何保证它在物理内存中是连续的?
@Nemo:谢谢你,事实上,当调用多个实例时,a.out
它们在开始时暂停,然后写入我收到oom-killer
消息的数组/var/log/messages
,确实你的sysctrl
命令阻止我首先启动太多a.out
实例。谢谢!
Jun 1 17:49:16 localhost kernel: [32590.293421] a.out invoked oom-killer: gfp_mask=0x280da, order=0, oomkilladj=0
Jun 1 17:49:18 localhost kernel: [32592.110033] kded4 invoked oom-killer: gfp_mask=0x201da, order=0, oomkilladj=0
Jun 1 17:49:20 localhost kernel: [32594.718757] firefox invoked oom-killer: gfp_mask=0x201da, order=0, oomkilladj=0
最后两行略显忧虑。:)
@doron:谢谢,很好的解释,抱歉不能投票/选择。