2014-12-07 2 views
1

Я кодирую некоторый критически важный код, который я должен убедиться, что он абсолютно свободен от утечек памяти. Я написал небольшую функцию, которая позволяет мне извлекать память во время выполнения, и я делаю измерения до и после выполнения некоторого кода (который должен быть утечкой), чтобы увидеть, остается ли использование памяти на том же уровне.Управление памятью в C++ STL в контейнерах

При отладке фрагмента кода, который «утечка», я, наконец, обнаружил, что виновником является векторный контейнер.

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

vector<char*>* v = new vector<char*>(); 
int n = 1024*1024; 
while (n--) 
{ 
    v->push_back(new char[256]()); // A 
} 
for (vector<char*>::iterator it=v->begin() ; it!=v->end() ; ++it) 
{ 
    delete[] (*it); 
} 
delete v; 

Если вы бежите, что код (инвалидизирующую оптимизация компилятора, конечно, -O0) и положить некоторую ловушку в конце, чтобы программа не выйдет (например, cin.ignore();), вы увидите, что ваша программа должна использовать около 20 Мб или около того памяти.

Я хотел бы понять, почему это происходит. Есть строка, которую я обозначил A, где, если вы выделите большой массив символов, вы увидите, что «оставшаяся» память в конце тоже больше. Я бы не назвал это утечкой как таковой, потому что, по-видимому, память могла быть повторно использована, если я выделяю и заполняю другой контейнер STL, но все же я ожидал, что эта память полностью освободится, когда код закончится.

Может кто-то пролить свет на то, почему эта память все еще используется? И как я могу «освободить» его по-настоящему?

Некоторые подробности о моей среде компилятора:

Using clang++: Apple LLVM version 6.0 (clang-600.0.51) (based on LLVM 3.5svn) 
Target: x86_64-apple-darwin13.3.0 
Thread model: posix 

Compiling with: g++ -std=c++11 -g -Wall -Wextra -Wpedantic -O0 main.cc -o main.out 
+0

Почему вы хотите использовать вектор вместо вектора >!? – Borgleader

+0

Я просто использую этот символ * для выделения произвольного количества памяти. На практике вы можете использовать любой тип там, и вы все равно будете наблюдать это поведение. – almosnow

+3

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

ответ

3

Во-первых, нет никакого оправдания для динамического распределения вашего контейнера в вашем сценарии. Зачем?

Во-вторых, это контейнер char*, и, хотя он несет ответственность за их управление, вы сохраняете полную и единоличную ответственность за то, что они указывают (или не указывают) на!

Рассмотрите возможность использования vector<char>, unique_ptr<char>, string или что-то подобное, что в качестве элемента типа, чтобы позволить vector взять эту задачу.

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

Если вы действительно хотите, чтобы убедиться, освободила память возвращается к ОС, то есть в основном два варианта (которые оба включают написание собственных аллокатора, или найти и использовать один кто-то другой встроенный):

  • Замените заменяемые глобальные функции распределения и освобождения на свои собственные.
  • Используйте свой собственный распределитель только для этих распределений (стандартные контейнеры имеют значение, соответствующее распределителю, что означает, что вы можете предоставить свой собственный распределитель для каждого контейнера).

После того, как все будет готово, попросите своего распределителя высвободить все обратно в ОС.

+0

Ударяет все правильные точки. Не использовать RAII безумным, если вы не должны гарантировать утечки, а измерение активной памяти очень ошибочно, когда тайники распределительного устройства не учитываются. – Puppy

+0

Это никак не связано с RAII. Вы можете попробовать написать свой собственный код и увидеть, что в итоге вы найдете то же поведение. Измерение памяти * может быть очень ошибочным, но вы, ребята, не имеете гарантии, что я делаю это правильно или неправильно. Я считаю, что я был достаточно ясен, когда объяснял общую ситуацию/поведение, чтобы такие комментарии не появлялись. В любом случае, спасибо Puppy за подсказку распределителя, я попытался бы реализовать пользовательский распределитель и посмотреть, смогу ли я избавиться от этой проблемы. – almosnow

+0

@almosnow: Да, вы не спрашиваете напрямую о RAII, и если вы уменьшили свой вопрос до того, что хотите на самом деле спросить (отсутствует сокращение памяти-footprint после 'delete'), я мог бы удалить первую часть. Это по-прежнему бесценный совет, и меня бы справедливо критиковали, если бы я не очень предупредил вас об этом. Тем не менее, я также отвечаю на вопрос, который вы действительно задали. – Deduplicator

Смежные вопросы