簡體   English   中英

在 C++ 中跟蹤內存使用情況並評估內存消耗

[英]Track Memory Usage in C++ and evaluate memory consumption

我的代碼遇到了以下問題:我使用 Valgrind 和 gperftools 執行堆檢查和堆分析,以查看是否釋放了我分配的所有內存。 這些工具的輸出看起來不錯,而且我似乎沒有失去記憶。 但是,當我查看topps的輸出時,我感到困惑,因為這基本上不代表我使用 valgrind 和 gperftools 觀察到的內容。

以下是數字:

  • 熱門報告: RES 150M
  • Valgrind (Massif) 報告: 23M峰值使用量
  • gperftools 堆分析器報告: 2270 萬峰值使用量

我現在的問題是,差異從何而來? 我也嘗試在 Valgrind 中跟蹤堆棧使用情況,但沒有任何成功。

更多細節:

  • 該過程基本上是通過 C api 從 mysql 加載數據到內存存儲
  • 在加載完成后不久執行泄漏檢查和中斷,顯示最終丟失了 144 個字節,並且 10M 可達,這符合當前分配的數量
  • 該庫不執行復雜的 IPC,它啟動了幾個線程,但只有一個線程正在執行工作
  • 它不加載其他復雜的系統庫
  • /proc/pid/smaps 中的 PSS 大小對應於 TOP 和 ps 中的 RES 大小

你有什么想法,報告的內存消耗的這種差異來自哪里? 如何驗證我的程序是否正常運行? 您對我如何進一步調查此問題有任何想法嗎?

最后,我能夠解決問題,並很樂意分享我的發現。 一般來說,從我的角度來看,評估程序內存消耗的最佳工具是 Valgrind 的Massif工具。 它允許您分析堆消耗並為您提供詳細分析。

要分析您的應用程序的堆,現在運行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 - 就我而言。 這意味着,您將使用posix_memalign分配 4b,但系統將為您分配整頁以正確對齊它。 如果您現在分配許多小值,您最終將獲得大量未使用的內存。 由於您只分配了此內存的一小部分,因此普通堆分析工具不會報告此內存,但系統分配例程將分配更多並隱藏其余部分。

為了解決這個問題,我切換到一個較小的邊界,從而可以大大減少內存開銷。

作為我在 Massif & Co 面前度過的時間的總結。我只能建議使用此工具進行深度分析,因為它可以讓您很好地了解正在發生的事情並允許輕松跟蹤錯誤。 對於posix_memalign的使用情況有所不同。 在某些情況下確實有必要,但是,在大多數情況下,使用普通的malloc就可以了。

根據這篇文章 ps/top 報告你的程序使用多少內存,如果它是唯一運行的程序。 假設您的程序例如使用一堆共享庫,例如已經加載到內存中的 STL,由於您的程序執行而分配的實際內存量與如果它會分配多少內存之間存在差距唯一的過程。

默認情況下,Massif 只報告堆大小。 TOP 報告內存中的實際大小,包括程序代碼本身使用的大小以及堆棧大小。

嘗試為 Massif 提供--stacks=yes選項,告訴它報告總內存使用情況,包括堆棧空間,看看這是否會改變圖片?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM