Отслеживание использования памяти в C ++ и оценка потребления памяти

Я столкнулся со следующей проблемой с моим кодом: я использовал Valgrind и gperftools для выполнения проверки кучи и профилирования кучи, чтобы увидеть, освобождаю ли я всю выделенную память. Вывод этих инструментов выглядит хорошо, и, похоже, я не теряю память. Тем не менее, когда я смотрю на top и выход ps Я запутался, потому что это в основном не соответствует тому, что я наблюдаю с помощью valgrind и gperftools.

Вот цифры:

  • Лучшие отчеты: RES 150M
  • Вальгринд (Массив) сообщает: 23М пиковое использование
  • gperftools Heap Profiler сообщает: 22.7M пиковое использование

Мой вопрос сейчас, откуда берется разница? Я также пытался отслеживать использование стека в Valgrind, но безуспешно.

Еще несколько деталей:

  • Процесс в основном загружает данные из mysql через API C в хранилище в памяти
  • Выполнение проверки на утечку и прерывание работы вскоре после завершения загрузки показывает окончательную потерю 144 байта и достижимость 10 М, что соответствует количеству, которое выделено в настоящее время.
  • Библиотека не выполняет сложный IPC, она запускает несколько потоков, но только один из потоков выполняет работу.
  • Он не загружает другие сложные системные библиотеки
  • размер PSS из / proc / pid / smaps соответствует размеру RES в TOP и ps

У вас есть идеи, откуда взялась эта разница в потребляемой памяти? Как я могу проверить, что моя программа работает правильно? У вас есть идеи, как я мог бы продолжить расследование этой проблемы?

15

Решение

Наконец я смог решить проблему и с радостью поделюсь своими выводами. В общем, лучшим инструментом для оценки потребления памяти программой с моей точки зрения является Массив инструмент от Valgrind. это позволяет вам профилировать потребление кучи и дает вам подробный анализ.

Для профилирования кучи вашего приложения запустите valgrind --tool=massif prog Теперь это даст вам базовый доступ ко всей информации о типичных функциях выделения памяти, таких как malloc и друзья. Однако, чтобы копать глубже, я активировал опцию --pages-as-heap=yes который затем сообщит даже информацию о нижележащих системных вызовах. Вот пример из моего сеанса профилирования:

 67  1,284,382,720      978,575,360      978,575,360             0            0
100.00% (978,575,360B) (page allocation syscalls) mmap/mremap/brk, --alloc-fns, etc.
->87.28% (854,118,400B) 0x8282419: mmap (syscall-template.S:82)
| ->84.80% (829,849,600B) 0x821DF7D: _int_malloc (malloc.c:3226)
| | ->84.36% (825,507,840B) 0x821E49F: _int_memalign (malloc.c:5492)
| | | ->84.36% (825,507,840B) 0x8220591: memalign (malloc.c:3880)
| | |   ->84.36% (825,507,840B) 0x82217A7: posix_memalign (malloc.c:6315)
| | |     ->83.37% (815,792,128B) 0x4C74F9B: std::_Rb_tree_node<std::pair<std::string const, unsigned int> >* std::_Rb_tree<std::string, std::pair<std::string const, unsigned int>, std::_Select1st<std::pair<std::string const, unsigned int> >, std::less<std::string>, StrategizedAllocator<std::pair<std::string const, unsigned int>, MemalignStrategy<4096> > >::_M_create_node<std::pair<std::string, unsigned int> >(std::pair<std::string, unsigned int>&&) (MemalignStrategy.h:13)
| | |     | ->83.37% (815,792,128B) 0x4C7529F: OrderIndifferentDictionary<std::string, MemalignStrategy<4096>, StrategizedAllocator>::addValue(std::string) (stl_tree.h:961)
| | |     |   ->83.37% (815,792,128B) 0x5458DC9: var_to_string(char***, unsigned long, unsigned long, AbstractTable*) (AbstractTable.h:341)
| | |     |     ->83.37% (815,792,128B) 0x545A466: MySQLInput::load(std::shared_ptr<AbstractTable>, std::vector<std::vector<ColumnMetadata*, std::allocator<ColumnMetadata*> >*, std::allocator<std::vector<ColumnMetadata*, std::allocator<ColumnMetadata*> >*> > const*, Loader::params const&) (MySQLLoader.cpp:161)
| | |     |       ->83.37% (815,792,128B) 0x54628F2: Loader::load(Loader::params const&) (Loader.cpp:133)
| | |     |         ->83.37% (815,792,128B) 0x4F6B487: MySQLTableLoad::executePlanOperation() (MySQLTableLoad.cpp:60)
| | |     |           ->83.37% (815,792,128B) 0x4F8F8F1: _PlanOperation::execute_throws() (PlanOperation.cpp:221)
| | |     |             ->83.37% (815,792,128B) 0x4F92B08: _PlanOperation::execute() (PlanOperation.cpp:262)
| | |     |               ->83.37% (815,792,128B) 0x4F92F00: _PlanOperation::operator()() (PlanOperation.cpp:204)
| | |     |                 ->83.37% (815,792,128B) 0x656F9B0: TaskQueue::executeTask() (TaskQueue.cpp:88)
| | |     |                   ->83.37% (815,792,128B) 0x7A70AD6: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.16)
| | |     |                     ->83.37% (815,792,128B) 0x6BAEEFA: start_thread (pthread_create.c:304)
| | |     |                       ->83.37% (815,792,128B) 0x8285F4B: clone (clone.S:112)
| | |     |
| | |     ->00.99% (9,715,712B) in 1+ places, all below ms_print's threshold (01.00%)
| | |
| | ->00.44% (4,341,760B) in 1+ places, all below ms_print's threshold (01.00%)

Как вы можете видеть, ~ 85% моей памяти выделяется из одной ветви, и теперь возникает вопрос, почему потребление памяти так велико, если первоначальное профилирование кучи показало нормальное потребление. Если вы посмотрите на пример, вы поймете, почему. Для размещения я использовал posix_memalign чтобы убедиться, что распределение происходит с полезными границами. Затем этот распределитель был передан от внешнего класса к внутренним переменным-членам (в данном случае — карте), чтобы использовать распределитель для выделения кучи. Однако граница, которую я выбрал, была слишком большой — 4096 — в моем случае. Это означает, что вы будете выделять 4b, используя posix_memalign но система выделит вам полную страницу, чтобы выровнять ее правильно. Если вы сейчас выделите много небольших значений, у вас будет много неиспользуемой памяти. Эта память не будет сообщаться обычными инструментами профилирования кучи, поскольку вы выделяете только часть этой памяти, но процедуры выделения системы будут выделять больше и прятать остальное.

Чтобы решить эту проблему, я переключился на меньшую границу и, таким образом, мог бы значительно сократить накладные расходы памяти.

В заключение мои часы, проведенные перед Массивом & Co. Я могу только рекомендовать использовать этот инструмент для глубокого профилирования, поскольку он дает вам очень хорошее понимание происходящего и позволяет легко отслеживать ошибки. Для использования posix_memalign ситуация другая. Есть случаи, когда это действительно необходимо, однако, в большинстве случаев вы просто будете в порядке с нормальным malloc,

15

Другие решения

В соответствии с этот article ps / top сообщает, сколько памяти использует ваша программа, если это была единственная запущенная программа. Предполагая, что ваша программа, например, использует кучу разделяемых библиотек, таких как STL, которые уже загружены в память, существует разрыв между объемом фактической памяти, которая выделяется из-за выполнения вашей программы, и объемом памяти, который он выделил бы, если бы это был единственный процесс.

2

По умолчанию массив сообщает только размер кучи. TOP сообщает фактический размер в памяти, включая размер, используемый самим кодом программы, а также размер стека.

Попробуйте снабдить Массив --stacks=yes вариант, сообщив ему об общем использовании памяти, включая пространство стека, и посмотреть, изменит ли это картину?

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector