簡體   English   中英

如何以及何時分配全局或 static 數組的 memory?

[英]How and when is the memory of a global or static array allocated?

在 c++ 中定義全局或 static 數組時,其 memory 不會在程序開始時立即保留,而只會在我們寫入數組時保留。 我發現令人驚訝的是,如果我們只寫入數組的一小部分,它仍然不會保留整個 memory。 考慮下面這個稀疏寫入全局數組的小例子:

#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;
}

現在針對不同的步長執行此操作,並查看頂部的 output,例如:

./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 列表示 memory 僅保留在小塊中,這也意味着陣列在物理 memory 中不太可能是連續的。 有人對較低層次的事物有更多的了解嗎?

這也有負面影響,我可以輕松運行許多程序,其中所有 VIRT 的總和超過物理 memory,只要 RES 的總和低於。 但是,一旦它們都寫入全局 arrays 系統就會用完物理 memory 並且程序會發送 sigkill 或其他東西。

理想情況下,我想告訴編譯器在開始時保留全局變量的 memory 和 static 變量。 可能的?

編輯

@Magnus:這些行實際上是正確的順序。 :) 以第一行為例./a.out 1000000表示我在數組中每百萬個條目寫入一次,因此總共只有 250 個。 這對應於只有 1868k 的 RES。 在最后一個示例./a.out 100每一百個條目被寫入,然后總的 memory 也被物理分配 RES=VIRT=1.9g。 從這些數字看來,每當一個條目被寫入陣列時,物理 memory 上都會保留一個完整的 4k 塊。

@Nawaz:該數組在虛擬地址空間中是連續的,但據我了解,操作系統可能很懶惰,僅在實際需要時才保留物理 memory。 由於這是在小塊中完成的,而不是一次性完成整個陣列,如何保證它在物理 memory 中是連續的?

@Nemo:謝謝你,事實上,當調用a.out的多個實例時,它在開始時暫停然后寫入數組,我在/var/log/messages中收到了oom-killer消息,實際上你的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:謝謝,很好的解釋,抱歉不能投票/選擇。

您正在查看正在提交的虛擬 memory 頁面。 操作系統通常只會在您的代碼明確寫入或讀取頁面時提交頁面。 這與 C++ 無關,它保證 arrays 是連續的。 如果您詢問如何讓您的操作系統在啟動時提交所有進程的頁面,您需要使用操作系統特定的東西(如果存在的話)。

這里有兩件事在起作用,即。 虛擬 memory 和物理 memory。

虛擬 memory 用於 static 數據,就像在程序開始執行之前分配程序的指令一樣。 我的意思是始終定義程序的地址。

操作系統可能很懶惰,但是在將 static 數據和程序指令加載到物理 memory RAM 時。 它的工作方式是這樣的:

  • 進程加載程序為 static 數據分配了進程虛擬 memory,但不將數據加載到 RAM 中。
  • 當嘗試訪問這些地址時,會觸發處理器異常,然后我們進入 kernel 模式。
  • kernel 現在將數據加載到 RAM 並將 RAM 鏈接到進程虛擬地址空間。
  • kernel 切換回用戶模式到發生處理器異常的確切位置。
  • 由於 RAM 現在已鏈接到進程虛擬地址空間,因此程序現在將繼續執行,就好像什么都沒發生過一樣。

這是操作系統被允許做的完全輕微的操作,因為它完全無法被正在運行的進程檢測到。 當然,除非我們缺少 memory。

我認為您發布的表格沒有任何實質意義。

As far as, array of static storage is concerned, its allocated before the start of the program, which by definition means, before the program enters into the main() function, the runtime allocates memory to the global arrays, and it lasts for duration程序:

§3.7.1/1

所有既沒有動態存儲期也不是本地的對象都有 static 存儲期。 這些對象的存儲應持續到程序的持續時間(3.6.2、3.6.3)。

而且無論是全局還是局部,arrays始終是連續的memory。

這聽起來像是一個 Linux 系統,一旦使用的 memory 超過可用的虛擬 memory,“OOM 殺手”就會喚醒並開始殺死進程。 Grep 用於 /var/log/messages 中的“oom”以確認。

如果是這樣,這個設置:

sysctl -w vm.overcommit_memory=2

...將阻止 kernel 允許您的進程分配比可用 VM 更多的資源。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM