Являются ли некоторые распределители ленивыми?

Я написал программу C в Linux, которая хранит память mallocs, запускает ее в цикле, а TOP не отображает потребление памяти.

то я сделал что-то с этой памятью, и TOP действительно показал потребление памяти.

Когда я malloc, я действительно "получаю память", или есть "ленивое" управление памятью, это только дает мне память, если/когда я его использую?

(Существует также опция, которую TOP знает только о потреблении памяти, когда я ее использую, поэтому я не уверен в этом.)

Спасибо

Ответы

Ответ 1

В Linux malloc запрашивает память с помощью sbrk() или mmap() - в любом случае, ваше адресное пространство разворачивается немедленно, но Linux не назначает фактические страницы физической памяти до первой записи на рассматриваемую страницу. Вы можете увидеть расширение адресного пространства в столбце VIRT, а фактическое использование физической памяти в RES.

Ответ 2

Да, память не отображается в ваше пространство памяти, если вы не коснетесь ее. mallocing memory будет настраивать только таблицы подкачки, чтобы они знали, когда вы получаете идентификатор страницы в выделенной памяти, память должна отображаться.

Ответ 3

Это немного начинается с темы (и затем я привяжу его к вашему вопросу), но то, что происходит, похоже на то, что происходит, когда вы разыгрываете процесс в Linux. При форкировании существует механизм, называемый copy on write, который копирует только пространство памяти для нового процесса, когда память также написана. Таким образом, если разветвленный процесс сразу же запускает новую программу, вы сохранили накладные расходы на копирование исходной памяти программ.

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

Какова цель этого? В основном это делает память mallocing более или менее постоянной операцией Big O (1) вместо операции Big O (n) (аналогично тому, как планировщик linux расширяет его, вместо того, чтобы делать это в одном большом фрагменте).

Чтобы продемонстрировать, что я имею в виду, я сделал следующий эксперимент:

[email protected]:~/test_code$ time ./bigmalloc 

real    0m0.005s
user    0m0.000s
sys 0m0.004s
[email protected]:~/test_code$ time ./deadbeef 

real    0m0.558s
user    0m0.000s
sys 0m0.492s
[email protected]:~/test_code$ time ./justwrites 

real    0m0.006s
user    0m0.000s
sys 0m0.008s

Программа bigmalloc выделяет 20 миллионов ints, но ничего не делает с ними. deadbeef записывает один int на каждую страницу, в результате чего в 19531 году записи и justwrites выделяют 19531 ints и нули их. Как вы видите, deadbeef занимает около 100 раз дольше, чем bigmalloc, и примерно в 50 раз дольше, чем justwrites.

#include <stdlib.h>    

int main(int argc, char **argv) {

int *big = malloc(sizeof(int)*20000000); // allocate 80 million bytes

return 0;

}

.

#include <stdlib.h>    

int main(int argc, char **argv) {

int *big = malloc(sizeof(int)*20000000); // allocate 80 million bytes

// immediately write to each page to simulate all at once allocation

// assuming 4k page size on 32bit machine

for ( int* end = big + 20000000; big < end; big+=1024 ) *big = 0xDEADBEEF ;    

return 0;

}

.

#include <stdlib.h>

int main(int argc, char **argv) {

int *big = calloc(sizeof(int),19531); // number of writes

return 0;
}

Ответ 4

Используете ли вы оптимизацию компилятора? Может быть, оптимизатор удалил выделение, так как вы не используете выделенные ресурсы?

Ответ 5

Эта функция называется overcommit - ядром "promises" вашей памяти, увеличивая размер сегмента данных, но не выделяет ему физическую память. Когда вы касаетесь адреса в этом новом пространстве, страница процесса вызывает ошибки в ядре, а затем пытается сопоставить ему физические страницы.

Ответ 6

Да, обратите внимание на флаги VirtualAlloc,

MEM_RESERVE
MEM_COMMIT

.

Хех, но для Linux или любой система POSIX/BSD/SVR # vfork() существует уже целую вечность и обеспечивает имитируемая функциональность.

Функция vfork() отличается от fork() только в том, что дочерний процесс могут совместно использовать код и данные с помощью вызывающий процесс (родительский процесс). Эта значительно увеличивает активность клонирования с угрозой целостности если vfork() используется неправильно.

Использование vfork() для любых целей кроме как прелюдия к немедленной вызов функции из exec семье или _exit(), не рекомендуется.

Функция vfork() может использоваться для создавать новые процессы без копирование адресного пространства старого обработать. Если разветвленный процесс просто вызов для вызова exec, пространство данных скопирован из родителя в ребенка fork() не используется. Это особенно неэффективны в выгружаемом среда, создающая vfork() особенно полезно. В зависимости от размер родительского пространства данных, vfork() может дать значительный улучшение производительности над fork().