При определении глобального или статического массива в 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: Спасибо, отличное объяснение, извините, не могу проголосовать / выбрать.