Я столкнулся со следующей проблемой с моим кодом: я использовал Valgrind и gperftools для выполнения проверки кучи и профилирования кучи, чтобы увидеть, освобождаю ли я всю выделенную память. Вывод этих инструментов выглядит хорошо, и, похоже, я не теряю память. Тем не менее, когда я смотрю на top
и выход ps
Я запутался, потому что это в основном не соответствует тому, что я наблюдаю с помощью valgrind и gperftools.
Вот цифры:
Мой вопрос сейчас, откуда берется разница? Я также пытался отслеживать использование стека в Valgrind, но безуспешно.
Еще несколько деталей:
У вас есть идеи, откуда взялась эта разница в потребляемой памяти? Как я могу проверить, что моя программа работает правильно? У вас есть идеи, как я мог бы продолжить расследование этой проблемы?
Наконец я смог решить проблему и с радостью поделюсь своими выводами. В общем, лучшим инструментом для оценки потребления памяти программой с моей точки зрения является Массив инструмент от 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
,
В соответствии с этот article ps / top сообщает, сколько памяти использует ваша программа, если это была единственная запущенная программа. Предполагая, что ваша программа, например, использует кучу разделяемых библиотек, таких как STL, которые уже загружены в память, существует разрыв между объемом фактической памяти, которая выделяется из-за выполнения вашей программы, и объемом памяти, который он выделил бы, если бы это был единственный процесс.
По умолчанию массив сообщает только размер кучи. TOP сообщает фактический размер в памяти, включая размер, используемый самим кодом программы, а также размер стека.
Попробуйте снабдить Массив --stacks=yes
вариант, сообщив ему об общем использовании памяти, включая пространство стека, и посмотреть, изменит ли это картину?