C++ std::vector ведет себя как утечка памяти в определенных ситуациях
Я нашел ситуацию, когда векторы ведут себя как утечка памяти и могут свести ее к минимальному рабочему примеру. В этом примере я делаю (в функции) вектор, который содержит три вектора char. Во-первых, эти векторы char выталкиваются большим количеством элементов, а их емкости сжимаются до их размеров. Затем над большими векторами назначаются векторы размером в один элемент. Проблема в том, что используемая память слишком велика и даже когда функция возвращает и векторы уничтожаются, память не освобождается. Как мне вернуть память? Почему это показывают это поведение? Что я могу сделать, чтобы избежать этой утечки?
вот пример кода (извините за длину):
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
using namespace std;
// see http://man7.org/linux/man-pages/man5/proc.5.html at /proc/[pid]/status
string meminfo() {
// memory information is in lines 11 - 20 of /proc/self/status
ifstream stat_stream("/proc/self/status",ios_base::in);
// get VmSize from line 12
string s;
for ( int linenum = 0; linenum < 12; ++linenum )
getline(stat_stream,s);
stat_stream.close();
return s;
}
void f() {
vector<vector<char>> mem(3); // with 1,2 memory is fine
size_t size = 16777215; // with 16777216 or greater memory is fine
for ( vector<char>& v : mem ) {
for ( unsigned int i = 0; i < size; ++i )
v.push_back(i);
v.shrink_to_fit(); // without this call memory is fine
}
cout << "Allocated vectors with capacities ";
for ( vector<char>& v : mem )
cout << v.capacity() << ", ";
cout << endl << "used memory is now: " << meminfo() << endl;
for ( vector<char>& v : mem ) {
v = vector<char>{1};
if ( v.size() != v.capacity() )
cout << "Capacity larger than size." << endl;
}
cout << "Shrinked vectors down to capacity 1." << endl
<< "Used memory is now: " << meminfo() << endl;
}
int main() {
cout << "At beginning of main: " << meminfo() << endl;
f();
cout << "At end of main: " << meminfo() << endl;
return 0;
}
и выход на моей машине:
At beginning of main: VmSize: 12516 kB
Allocated vectors with capacities 16777215, 16777215, 16777215,
used memory is now: VmSize: 78060 kB
Shrinked vectors down to capacity 1.
Used memory is now: VmSize: 61672 kB
At end of main: VmSize: 61672 kB
однако valgrind не видит утечки памяти.
Я думаю, что параметры в Примере зависят от системы, чтобы показать странный bahaviour. Я использую Linux Mint Debian Выпуск с ядром g++ 4.8.2 и x86_64. Я компилирую с:
g++ -std=c++11 -O0 -Wall memory.cpp -o memory
и попробовал также-O3 и никаких явных настроек для оптимизации.
некоторые интересные моменты:
- когда я заменить
v = vector<char>{1};
byv.clear(); v.shrink_to_fit(); v.push_back(1);
проблема остается той же. Замена нажатия и сжатия для больших векторов наv = vector<char>(16777215);
"решает" проблему памяти. - 16777215 = 2^24 - 1, так может, это что-то с памятью границы государств.
- кроме того, можно было бы ожидать от памяти программы, используются в начале основной (12516 КБ) плюс память о больших векторов, которые будут использоваться в общей сложности примерно 3*16777216 Б + 12516 КБ = 61668 КБ, что примерно памяти он использует в конце.
в реальном приложении я использую векторы для сбора операций, которые должны применяться к матрице жесткости моделирования FEM. С тех пор, как я хочу дойти до предела. из того, что возможно с доступной памятью (также с точки зрения скорости), мне нужно сохранить несвободную память, чтобы избежать замены. Поскольку обмен действительно происходит, я предполагаю, что значение VmSize является надежным.
2 ответов
проблема в том, что вы неправильно понимаете значение "освобождения" памяти в контексте C++. Когда ваше приложение освобождает память (используя shrink_to_fit
или удаление объектов или что-то еще), его на самом деле просто выпускает память в среду выполнения C++ и не обязательно выпускает ее обратно в систему для других процессов. Среда выполнения C++ может выбрать сохранение памяти для повторного использования позже в том же процессе.
обычно это происходит, когда память фрагментирована -- свободная память окружена (в пространстве виртуальной машины программы) встроенной памятью. Только когда освобожденная память находится в конце пространства памяти программы, среда выполнения C++ выберет (или сможет) вернуть ее в систему.
Как правило, это сохранение памяти не является проблемой, так как обычно его можно повторно использовать, когда приложение запрашивает больше памяти. Проблема может быть в том, что, поскольку среда выполнения C++ не может перемещаться по используемому блоку памяти, вы не сможете повторно использовать бесплатно куски, которые слишком малы. Существуют всевозможные приемы и эвристики, что среда выполнения может использовать, чтобы попытаться избежать ситуации, но они не всегда работают.
просто примите во внимание, что вы не имеете дело непосредственно с подпрограммами выделения памяти ОС. Обычно есть другой менеджер памяти в середине, выполняющий эту работу за вас.
ОС должна присутствовать на запросе от всех процессов, запущенных в машине, поэтому не рекомендуется много раз запрашивать небольшие куски памяти. Лучше просить большие куски меньше раз. Менеджер памяти, работающий в вашем приложении, будет хранить некоторые куски памяти в надежде, что они понадобятся вашей заявке позже.