[C++] Куда уходит память?
Память на старте приложения: 480 К.
После отработки: 72 540 К
При этом совокупный размер всех строк в результирующем векторе: 6 908 733
Сумма capacity всех строк и самого вектора: 8 468 К
Куда делась остальная память?
#include <vector>
#include <algorithm>
std::vector<std::wstring> GetVariants()
{
std::vector<std::wstring> v;
v.push_back(L"a");
v.push_back(L"b");
v.push_back(L"c");
return v;
}
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
std::vector<std::wstring> v(1);
for (size_t i = 0; i < 12; ++i)
{
std::vector<std::wstring> vCopy = v;
std::vector<std::wstring> vVariants = GetVariants();
//first
for (size_t j = 0; j < v.size(); ++j)
{
v[j] += vVariants[0];
}
//remains
for (size_t j = 1; j < vVariants.size(); ++j)
{
copy(vCopy.begin(), vCopy.end(), back_inserter(v));
for (size_t k = v.size() - vCopy.size(); k < v.size(); ++k)
{
v[k] += vVariants[j];
}
}
}
size_t size = 0;
size_t capacity = 0;
for (size_t i = 0; i < v.size(); ++i)
{
size += v.size();
capacity += v.capacity();
}
size += v.size();
capacity += v.capacity();
cout << "size: " << size << endl << "capacity: " << capacity / 1024 << " K"<< endl;
return 0;
}
Память на старте приложения: 480 К.
После отработки: 72 540 К
Как ты это узнал? По диспетчеру задач?
При этом совокупный размер всех строк в результирующем векторе: 6 908 733
Как ты это узнал? Если ориентируешься по тому size, что ты выводишь - то это не размер всех строк.
Куда делась остальная память?
У тебе тут выделяется уйма временных объектов, на которых тоже тратится память. Учитывая, что ты замерял непонятным способом - вполне возможно что тебе выделили нужное под все эти объекты количество памяти и именно оно и показывается в тебя в таск менеджере, а не реально используемая процессом память. Так бывает. )
Ну да, согласен. Еще sizeof(wchar_t) надо учитывать.
Смотрю по диспетчеру задач. Пусть не совсем точно.
Но это не суть. Цифры же отличаются на порядки. Если увеличить количество итераций цикла хотя бы раза в два - получаю bad alloc memory на каком-то этапе.
И еще посчитай сколько итерацией циклов у тебя прокручивается - там очень немаленькие числа. И теперь представь сколько там создается временных объектов, происходят их копирования постоянные, реаллокации памяти. Очень жесткий код в этом плане. )
Немного изменил текст
1.cpp:
int main(int argc, char* argv[])
... // в конце перед return
int a;
cin >> a;
компилируемся обычно
26227: ./1
0000000000400000 16K r-x-- /home/user/1
0000000000603000 4K r---- /home/user/1
0000000000604000 4K rw--- /home/user/1
0000000001a58000 73404K rw--- [ anon ]
00007fa3e66d1000 8196K rw--- [ anon ]
00007fa3e74d4000 1396K r-x-- /lib64/libc-2.12.2.so
00007fa3e7631000 2044K ----- /lib64/libc-2.12.2.so
00007fa3e7830000 16K r---- /lib64/libc-2.12.2.so
00007fa3e7834000 4K rw--- /lib64/libc-2.12.2.so
00007fa3e7835000 20K rw--- [ anon ]
00007fa3e783a000 84K r-x-- /lib64/libgcc_s.so.1
00007fa3e784f000 2044K ----- /lib64/libgcc_s.so.1
00007fa3e7a4e000 4K r---- /lib64/libgcc_s.so.1
00007fa3e7a4f000 4K rw--- /lib64/libgcc_s.so.1
00007fa3e7a50000 512K r-x-- /lib64/libm-2.12.2.so
00007fa3e7ad0000 2044K ----- /lib64/libm-2.12.2.so
00007fa3e7ccf000 4K r---- /lib64/libm-2.12.2.so
00007fa3e7cd0000 4K rw--- /lib64/libm-2.12.2.so
00007fa3e7cd1000 944K r-x-- /usr/lib64/gcc/x86_64-pc-linux-gnu/4.5.3/libstdc++.so.6.0.14
00007fa3e7dbd000 2044K ----- /usr/lib64/gcc/x86_64-pc-linux-gnu/4.5.3/libstdc++.so.6.0.14
00007fa3e7fbc000 32K r---- /usr/lib64/gcc/x86_64-pc-linux-gnu/4.5.3/libstdc++.so.6.0.14
00007fa3e7fc4000 8K rw--- /usr/lib64/gcc/x86_64-pc-linux-gnu/4.5.3/libstdc++.so.6.0.14
00007fa3e7fc6000 84K rw--- [ anon ]
00007fa3e7fdb000 120K r-x-- /lib64/ld-2.12.2.so
00007fa3e81c6000 20K rw--- [ anon ]
00007fa3e81f5000 12K rw--- [ anon ]
00007fa3e81f8000 4K r---- /lib64/ld-2.12.2.so
00007fa3e81f9000 4K rw--- /lib64/ld-2.12.2.so
00007fa3e81fa000 4K rw--- [ anon ]
00007fff7e6ab000 132K rw--- [ stack ]
00007fff7e7ff000 4K r-x-- [ anon ]
ffffffffff600000 4K r-x-- [ anon ]
total 93220K
наблюдаем 75 мегабайт анонимной памяти, то есть которая как бы есть, но ее на самом деле нет... приложение под себя зарезервировало. смотрим далее на strace...
execve("./1", ["./1"], [/* 54 vars */]) = 0
brk(0) = 0x6de000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f936aed8000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
... <skipped>
mprotect(0x7f936a511000, 16384, PROT_READ) = 0
mprotect(0x7f936a72f000, 4096, PROT_READ) = 0
mprotect(0x7f936a9b0000, 4096, PROT_READ) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f936aea7000
mprotect(0x7f936ac9d000, 32768, PROT_READ) = 0
mprotect(0x603000, 4096, PROT_READ) = 0
mprotect(0x7f936aed9000, 4096, PROT_READ) = 0
munmap(0x7f936aeac000, 179876) = 0
brk(0) = 0x6de000
brk(0x6ff000) = 0x6ff000
brk(0x723000) = 0x723000
brk(0x744000) = 0x744000
brk(0x765000) = 0x765000
brk(0x786000) = 0x786000
... <skipped>
brk(0x4d64000) = 0x4d64000
brk(0x4d85000) = 0x4d85000
brk(0x4da6000) = 0x4da6000
brk(0x4dc7000) = 0x4dc7000
brk(0x4de8000) = 0x4de8000
brk(0x4e09000) = 0x4e09000
brk(0x4e2a000) = 0x4e2a000
brk(0x4e4b000) = 0x4e4b000
brk(0x4e6c000) = 0x4e6c000
brk(0x4e8d000) = 0x4e8d000
fstat(1, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f936aed7000
write(1, "size: 6908733\n", 14) = 14
write(1, "capacity: 7251 K\n", 17) = 17
munmap(0x7f93693b2000, 8392704) = 0
exit_group(0) = ?
обращаем внимание на системный вызов brk - изменение размера сегмента данных программы.
по полному выводу данный вызов был сделан у меня 558 раз (я его не привожу полностью)
доходим до относительно адреса 4e8d000, что примерно 75 мегабайт.
вот он и размер.
не стал я лезть в исходники STL, ох уж это неблагодарное дело
но полагаю, что либо в системе, либо в STL работает низкоуровневый механизм, который пытается предсказать, сколько памяти еще потребуется приложению в близжайшее время и пытается сразу эту памяти резервировать на ближайшее использование (вероятно работает оптимизатор). не могу это подтвердить или опровергнуть - нет возможности дальше глубоко копать.
может кто квалифицровано напишет об этом или даст ссылку.
С учетом размера wchar_t и в килобайтах
size_t capacity = 0;
for (size_t i = 0; i < v.size(); ++i)
{
size += v.size();
capacity += v.capacity();
}
size += v.size();
capacity += v.capacity();
size = size / 1024 * sizeof(wchar_t);
capacity = capacity / 1024 * sizeof(wchar_t);
size: 13 492 K
capacity: 16 936 K
диспетчер задач (в момент остановки на cout): 72 540 K (в пиках во время работы было до 90)
Про временные объекты - это понятно, не понятно, почему они не освобождаются между итерациями цикла.
PS Кстати, поэкспериментировал с листом - получается примерно то же самое
PS: Занимаясь нетрадиционным сексом стоит помнить о последствиях.
const size_t MAX_STRINGS = 500000;
//-- variant 0
wchar_t *v = new wchar_t [MAX_STRINGS * MAX_SYMBOLS];
//-- variant 1
wchar_t **v = new wchar_t* [MAX_STRINGS];
for (size_t i = 0; i < MAX_STRINGS; ++i)
{
v = new wchar_t [MAX_SYMBOLS];
}
//-- variant 2
std::wstring ws(MAX_SYMBOLS, L'0');
std::vector<std::wstring> v(MAX_STRINGS, ws);
//-- variant 3
std::vector<wchar_t> ws(MAX_SYMBOLS);
std::vector< std::vector<wchar_t> > v(MAX_STRINGS, ws);
вариант 0: 10 244 К
вариант 1: 41 458 К
вариант 2: 63 104 К
вариант 3: 49 392 К
const size_t MAX_STRINGS = 500000;
//-- variant 0
wchar_t *v = new wchar_t [MAX_STRINGS * MAX_SYMBOLS];
//-- variant 1
wchar_t **v = new wchar_t* [MAX_STRINGS];
for (size_t i = 0; i < MAX_STRINGS; ++i)
{
v = new wchar_t [MAX_SYMBOLS];
}
//-- variant 2
std::wstring ws(MAX_SYMBOLS, L'0');
std::vector<std::wstring> v(MAX_STRINGS, ws);
//-- variant 3
std::vector<wchar_t> ws(MAX_SYMBOLS);
std::vector< std::vector<wchar_t> > v(MAX_STRINGS, ws);
вариант 0: 10 244 К
вариант 1: 41 458 К
вариант 2: 63 104 К
вариант 3: 49 392 К
Здесь все доп. расходы более чем естественны.