Как и когда выделяется память глобального или статического массива? - PullRequest
4 голосов
/ 01 июня 2011

При определении глобального или статического массива в 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 или что-то в этом роде.

В идеале я бы хотел сказать компилятору зарезервировать память глобальным и статическимпеременные в начале.Возможно?

Редактировать

@ Магнус: Линии на самом деле в правильном порядке.:) Например, первая строка ./a.out 1000000 означает, что я записываю каждую миллионную запись в массиве, а значит всего 250.Это соответствует RES только 1868k.В последнем примере ./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: Спасибо, отличное объяснение, извините, не могу проголосовать / выбрать.

Ответы [ 4 ]

6 голосов
/ 01 июня 2011

Вы смотрите, что страницы виртуальной памяти фиксируются.ОС обычно фиксирует страницы только тогда, когда они написаны или прочитаны вашим кодом.Это не имеет ничего общего с C ++, который гарантирует непрерывность массивов.Если вы спрашиваете, как заставить вашу ОС фиксировать все страницы вашего процесса при запуске, вам нужно использовать специфические для ОС вещи (если таковые существуют).

2 голосов
/ 02 июня 2011

Здесь есть две вещи, а именно. виртуальная память и физическая память.

Виртуальная память для статических данных, как и инструкции для вашей программы, назначаются до начала выполнения вашей программы. Под этим я подразумеваю, что адрес вашей программы всегда определяется.

Операционная система может быть ленивой, однако когда дело доходит до загрузки как статических данных, так и программных инструкций в ОЗУ физической памяти. Вот как это работает:

  • Загрузчик процесса назначил виртуальную память процесса для статических данных, но не загружает данные в ОЗУ.
  • Когда кто-то пытается получить доступ к этим адресам, запускается исключение процессора, и затем мы входим в режим ядра.
  • Теперь ядро ​​загружает данные в ОЗУ и связывает ОЗУ с виртуальным адресным пространством процессов.
  • Ядро переключается обратно в пользовательский режим в точную точку, где произошло исключение процессора.
  • Поскольку оперативная память теперь связана с виртуальным адресным пространством процессов, программа продолжит работу, как будто ничего не произошло.

Это полная слабость, которую операционная система может делать просто потому, что она совершенно не обнаружима для запущенного процесса. Если, конечно, нам не хватает памяти.

1 голос
/ 01 июня 2011

Это звучит как система Linux, где "OOM killer" просыпается и запускает процессы уничтожения, когда используемая память превышает доступную виртуальную память. Grep для "oom" в / var / log / messages для подтверждения.

Если это так, этот параметр:

sysctl -w vm.overcommit_memory=2

... не позволит ядру разрешить вашим процессам выделять больше доступной виртуальной машины.

1 голос
/ 01 июня 2011

Я не думаю, что таблица, которую вы разместили, доказывает что-либо существенное.

Что касается массива статической памяти, то он выделяется перед запуском программы, что по определению означает, что перед тем, как программа входит в функцию main(), среда выполнения выделяет память глобальным массивам Продолжительность программы:

§3.7.1 / 1

Все объекты, которые не имеют динамического длительность хранения, ни местные статическая продолжительность хранения. Хранение для этих объектов будет продолжаться для продолжительность программы (3.6.2, 3.6.3).

И является ли он глобальным или локальным, массивы всегда имеют непрерывную память.

...