заполнение памяти с помощью realloc

Вопрос может показаться немного тривиальным, я пытаюсь написать программу на C, которая просто съедает память столько, сколько может, прежде чем OOM будет вызван и убьет ее. Хотя изначально я использовал malloc() с memset(), я решил попробовать realloc() на этот раз. Я делаю это исключительно для обучения, так как я новичок в C.

Я намерен выделять 1 МБ при каждом вызове realloc() и memset(). Когда я запускаю эту программу для выделения 20 МБ:

1) Я не понимаю, почему в выводе некоторые адреса одинаковы (2-4) (5-10) и (11-20)? Разве каждый из них не должен быть другим.

2) Действительно ли моя программа потребляет 20 МБ памяти? Я прогнал его через Valgrind, и он говорит: «22 020 096 байт в 1 блоке определенно потеряны в записи о потерях 1 из 1».

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <error.h>
#include <unistd.h>

int main (int argc,char **argv) {
char *ptr;
int max = 0;
int max_write;
int sleep_time;
size_t size = 1048576;
if (argc > 1) {
    sleep_time = (argv[2] ? atoi(argv[2]) : 2 ); 
    max_write  = (argv[1] ? atoi(argv[1]) : 1000);
    printf (" + To Write: %d,%d\n",max_write,sleep_time);
}
ptr = (char *) calloc (1,size);
if (ptr == NULL) {
    perror("calloc Error:");
}
printf(" + Allocation: %p\n",ptr);
do { 
    max++;
    size += (1048576);
    ptr = (char *) realloc (ptr,size);
    if (ptr == NULL) {
        perror("realloc Error:");
    }
    memset(ptr,0,size);
    printf(" + Pointer: %p / Memory: %d\n",ptr,max);
} while (max != max_write);
//
return(0);
}



OUTPUT:
./eatmemory 20 
+ Allocation: 0x7f2bb6b12010
+ Pointer: 0x7f2bb6451010 / Memory: 1
+ Pointer: 0x7f2bb6150010 / Memory: 2
+ Pointer: 0x7f2bb6150010 / Memory: 3
+ Pointer: 0x7f2bb6150010 / Memory: 4
+ Pointer: 0x7f2bb5b4f010 / Memory: 5
+ Pointer: 0x7f2bb5b4f010 / Memory: 6
+ Pointer: 0x7f2bb5b4f010 / Memory: 7
+ Pointer: 0x7f2bb5b4f010 / Memory: 8
+ Pointer: 0x7f2bb5b4f010 / Memory: 9
+ Pointer: 0x7f2bb5b4f010 / Memory: 10
+ Pointer: 0x7f2bb4f4e010 / Memory: 11
+ Pointer: 0x7f2bb4f4e010 / Memory: 12
+ Pointer: 0x7f2bb4f4e010 / Memory: 13
+ Pointer: 0x7f2bb4f4e010 / Memory: 14
+ Pointer: 0x7f2bb4f4e010 / Memory: 15
+ Pointer: 0x7f2bb4f4e010 / Memory: 16
+ Pointer: 0x7f2bb4f4e010 / Memory: 17
+ Pointer: 0x7f2bb4f4e010 / Memory: 18
+ Pointer: 0x7f2bb4f4e010 / Memory: 19
+ Pointer: 0x7f2bb4f4e010 / Memory: 20

person hmmm    schedule 19.10.2014    source источник


Ответы (3)


1) Я не понимаю, почему в выводе некоторые адреса одинаковы (2-4) (5-10) и (11-20)? Разве каждый из них не должен быть другим.

Как уже говорили другие, realloc не обязательно должен перемещать существующий фрагмент памяти, но может просто расширить его.

2) Действительно ли моя программа потребляет 20 МБ памяти? Я прогнал его через Valgrind, и он говорит: «22 020 096 байт в 1 блоке определенно потеряны в записи о потерях 1 из 1».

Это несколько зависит от реализации, но обычно в конечном итоге вы требуете больше памяти, чем просили. Во-первых, free нужны некоторые метаданные о том, как объединить память с соседними свободными фрагментами. Эта информация часто находится в паре байтов прямо перед адресом, который вернет alloc/realloc. Кроме того, память может быть организована не так, чтобы можно было выделить произвольные размеры, поэтому malloc просто вернет тот, который подходит лучше всего.

person lnvd    schedule 19.10.2014

Как сказано в документации для realloc() и объяснено далее в этот ответ SO ,

Функция может переместить блок памяти в новое место

Это означает, что он может вернуть тот же указатель, если диспетчер памяти сможет выделить запрошенный размер в той же точке памяти.

Что касается (2), что именно вы имеете в виду под "Действительно ли выделяется 20 МБ?" Если вы спросите realloc() о 20 МБ, вы получите блок как минимум такого размера или NULL, если это не удастся. Если бы это было не так, это полностью разрушило бы цель функции.

person uesp    schedule 19.10.2014
comment
извините, я имел в виду, что моя программа действительно потребляла 20 МБ памяти при запуске. Я не имел в виду запрос realloc() для 20 МБ. - person hmmm; 20.10.2014
comment
Поскольку вы фактически записываете всю память, используя memset(), тогда да, вы фактически потребляете 20 МБ памяти. - person uesp; 20.10.2014
comment
Спасибо, это имеет смысл. Последний вопрос, который у меня был, был ли эти адреса действительно из кучи? Я думал, что они больше похожи на стек для меня. Могу ли я проверить, принадлежит ли адрес куче или кадру стека? - person hmmm; 20.10.2014
comment
Ознакомьтесь с: stackoverflow.com/questions/16709946/ - person uesp; 20.10.2014

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

Что он делает на самом деле:

  1. Хорошо, у меня есть указатель, размер которого нужно изменить на X. Сколько памяти осталось сразу после этого указателя?

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

  3. Если Y меньше X, то он сюда не влезет. Вероятно, есть еще одна переменная, которая мешает мне расширить указатель на месте. Хорошо, давайте переместим его куда-нибудь вместе со всеми данными. Помните, что мой указатель должен указывать на непрерывную область памяти.

Я действительно не понимаю вашего вопроса о 20 МБ. Вы беспокоитесь, что это займет немного больше, чем это? Не могу сказать, что знаю причину, но попробуйте с другой итерацией и проверьте, меняются ли результаты valgrind: может быть, это вопрос некоторой оптимизации времени выполнения и предварительного выделения большего пространства? Но это слепое предположение, если честно.

person Piotr Zierhoffer    schedule 19.10.2014
comment
Спасибо за объяснение ! - person hmmm; 20.10.2014