【Android Linux內存及性能優化】(七) 程序內存泄漏檢查工具

本文接着
【Android Linux內存及性能優化】(一) 進程內存的優化 - 堆段
【Android Linux內存及性能優化】(二) 進程內存的優化 - 棧段 - 環境變量 - ELF
【Android Linux內存及性能優化】(三) 進程內存的優化 - ELF執行文件的 數據段-代碼段
【Android Linux內存及性能優化】(四) 進程內存的優化 - 動態庫- 靜態庫
【Android Linux內存及性能優化】(五) 進程內存的優化 - 線程
【Android Linux內存及性能優化】(六) 系統內存的優化

一、內存篇

1.1 系統當前可用內存

1.2 進程的內存使用

1.3 進程內存優化

1.4 系統內存優化

1.5 內存泄漏

解決內存泄漏,一個最好的辦法就是,不要讓你的進程成爲一個守護進程,完成工作後立刻退出,Linux 會自動回收該進程所佔有的內存,這樣你寫的程序即使存在一些內存泄漏,也無關大局。

1.5.1 mtrace() 和 muntrace()

首先介紹glibc 針對內存泄漏給出的解決方法, 在glibc 庫中實現了一個鉤子函數mtrace,mtrace的使用方法如下:

  1. 加入頭文件<mcheck.h>
  2. 在需要內存泄漏檢查的代碼的開始調用 void mtrace(),在需要內存泄漏檢查代碼結尾調用 void muntrace()
    一般情況下不要調用 muntrace,而讓程序自然結束,因爲有些內存釋放代碼要到 muntrace 之後運行。
  3. 用 debug 模式編譯檢查代碼(-g 或 -ggdb)
  4. 在運行程序之前,先設置環境變量MALLOC_TRACE 爲一文件名,這一文件將有內存分配信息。
  5. 運行程序,內存分配的log 將輸出到 MALLOC_TRACE 所指向的文件中。

但實際上使用這程方法有兩個問題:

  • Log 文件增長得很大

  • 程序運行很慢
    因爲程序要把日誌寫到 Flash 上,而讀寫 Flash 是比較慢的,可以想辦法證mtrace 的日誌輸出在終端窗口,由終端軟件記錄下來。
    如: export MALLOC_TRACE=/dev/stdout
    有些設備沒有 stdout ,可以設置 export MALLOC_TRACE=/proc/self/fd/1 來實現

    另外:mtrace 函數內,試圖根據調用 malloc 的代碼指針,解析出對應的函數,這會導致進程的速諱莫如深變得很慢。

1.5.2 malloc() 和 free() 鉤子函數

在glibc 中,提供了 malloc 、free、realloc、memalign 的鉤子函數。
可以按照鉤子函數的原型,定義自已的函數,並在glibc 中設置相應的鉤子函數,這樣glibc 在處理內存相關的函數時,會調用鉤子函數,從而獲得相應的信息。


1.5.3 化整爲零法

一般對守護進程纔會花大力氣去檢測泄漏。對於進程仙存的優化,首先需要考慮爭取其不是守護進程,或者簡化其守護的部分。

將某些相對獨立、對內存影響較大的部分,分離出來通過進程來實現,這樣業務邏輯完成後,其所佔用的內存會隨進程的銷燬而得到釋放。

因此,可以考慮將守護進程中,很多相慶的模塊採用子進程的方式來實現,從而減少守護進程本身的復結性。


1.5.4 Dmalloc

dmalloc 是一個開源工具,其具體使用方法如下:

  1. 從 www.dmalloc.com 上下載dmalloc 的源代碼。
  2. 按照嵌入式平臺的要求對其進行編譯,生成靜態鏈接庫 libdmalloc.a
  3. 在需要檢測的源代碼中,包含 dmalloc.h: #include <dmalloc.h>
  4. 重新編譯程序代碼,gcc -ldmalloc -o program code.c
  5. 在嵌入式設備中,設置環境變量,定義log 的輸出文件
    export DMALLOC_options=log=logname, debug=0x3
  6. 運行程序,待程序退出後,會將內存信息輸出到日誌文件中。

dmalloc 的原理:
在這裏插入圖片描述
dmalloc 通過宏定義,將代碼中的malloc 、realloc 等函數轉到自身定義的函數中,從而獲得了控制權,運用自身的代碼去跟蹤和檢測堆內存的分配和釋放。

dmalloc 的優點是:

  • 運行簡單、能夠將內存分配和代碼行對應起來。

其侷限是:

  • 需要修改所有使用 malloc 等函數的文件,對於大項目來說,使用起來非常麻煩。
  • 必須擁有源碼。
  • 只能等待進程運行結束後才能獲得結果,這對守護進程不適用。
  • 雖然能夠獲得內存分配的日誌記錄,精確到具體文件具體行,但是沒有如何調用到該函數的棧信息。
  • 使用dmalloc 將增強系統的健狀性,但不能完全檢測出所有的內存錯誤。

1.5.5 Valgrind

Valgrind 是一款非常強大的產品,它可以幫助程序員檢測無效的內存分配、訪問未初始化的內存、內存泄漏等。

Valgrind 包含一個核心,它提供了一個虛擬的CPU 運行程序,還有一系列的工具,它們完成調試、剖析和 一些類似的任務。

它的一個優點在於,它是對程序二進制代碼進行跟蹤、調試,不必修改代碼、重新編譯等,使用起來極其方便。

valgrind官網:http://valgrind.org。

valgrind 包含幾個標準的工具,分別如下:

1.5.5.1 memcheck

memcheck 控測程序中的內存管理存在的問題。
它檢查所有對應內存的讀寫操作,並截取所有的malloc /new/free/delete 調用,

因此,memcheck 工具能夠控測以下問題:
(1)使用未初始化的內存。
(2)讀/寫 已結被釋放的內存。
(3)讀/寫 內存越界。
(4)讀/寫 不恰當的內存棧空間。
(5)內存泄漏。
(6)使用 malloc/ new/ new[] 和 free / delete / delete[] 不匹配。


1.5.5.2 cachegrind

cachegrind 是一個 Cache 剖析器。
它模擬執行 CPU 中的 L1、D1 和 L2 Cache,因此它能很精確地指出代碼中的Cache 未命中。

如果需要,它可以打印出Cache 中未命中的次數,內存引用和發生 Cache 未命中的每一行代碼、每一個函數、每一個模塊 和 整個程序的摘要。
如果追求更細緻的信息,它可以打印出每一行機器碼的未命中次數。

在X86 和 ADM64 上,cachegrind 通過 CPUID 自動探測機器的Cache 配置,所以存多數情況下它不再需要更多的配置信息了。


1.5.5.3 valgrind

valgrind 查找多線程程序中的競爭數據。
它可以查找那些被多個線程訪問但沒有使用同步鎖的內存地址。
這表示這些地址在多線程訪問的時候沒有進行同步,很可能會引起很難查找的時序問題。

valgrind 使用方法如下:
valgrind --tool=tool_name program_name

比如,對ls -l 命令做內存檢查,只需要執行下面的命令就可以了:
valgrind --tool=memcheck ls -l

如果要檢查內存泄漏,只需要增回 --leak-check=yes 就 可以了,如下:
valgrind --tool=memcheck --leak-check=yes ls -l


不同工具間加入的代碼變化非常大。在每個作用域的末尾,memcheck 加入代碼檢查每個片內存的訪問和進行值計算,代碼大小至少增加 12 倍,運行速度要比平時慢 25-50 倍。


valgrind 模擬程序中的每一條指令執行,因此檢查工具和剖析工具不僅對你的應用程序,還對共享庫、GNU C庫、X的客戶端都起作用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章