如何以及何时分配全局或静态数组的内存?

tom*_*tom 4 c++ arrays static global contiguous

在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;
}
Run Code Online (Sandbox Code Playgroud)

现在为不同的步长执行此操作并查看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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

最后两行略显令人担忧.:)

@doron:谢谢,很好的解释,抱歉不能upvote /选择.

小智 6

您正在查看正在提交的虚拟内存页面.操作系统通常只会在您的代码明确写入或读取页面时提交页面.这与C++无关,后者保证数组是连续的.如果您在启动时询问如何让操作系统提交所有进程的页面,则需要使用特定于操作系统的内容(如果存在).