導(dǎo)讀|遭受內(nèi)存泄露往往是令開發(fā)者頭疼的問題,傳統(tǒng)分析工具 gdb、Valgrind在解決內(nèi)存泄露問題上效率較低。本文特別邀請到了騰訊后臺開發(fā)工程師邢孟棒以 TDSQL實際生產(chǎn)中mysql-proxy內(nèi)存泄露問題作為分析對象,分享其基于動態(tài)追蹤技術(shù)的通用內(nèi)存泄露(增長)分析方法。其中將詳細(xì)介紹內(nèi)存分配器行為分析、缺頁異常事件分析,涵蓋應(yīng)用程序內(nèi)存分配的常見過程。閱讀完本文后,開發(fā)者僅需關(guān)注少數(shù)可能導(dǎo)致內(nèi)存泄露的代碼路徑,就能有效提升定位內(nèi)存泄露(增長)問題的效率。
背景某個 TDSQL 私有化環(huán)境中, 中間件 mysql-proxy 進(jìn)行大量請求轉(zhuǎn)發(fā)時,內(nèi)存占用量持續(xù)增長導(dǎo)致 OOM 現(xiàn)象,最終影響了用戶業(yè)務(wù)的正常使用 。本人分析該問題的過程中發(fā)現(xiàn)一個較為普遍的業(yè)務(wù)痛點:傳統(tǒng)分析工具(gdb、Valgrind 等)效率相對較低,在私有化場景中尤其突出。針對這一痛點,我將提供相對通用的內(nèi)存泄露(增長)分析方法,協(xié)助各位開發(fā)者更高效地定位發(fā)生泄露的代碼路徑,以期最大化減少人力投入成本并降低對用戶業(yè)務(wù)體驗的影響。
基礎(chǔ)概念
在展開講述內(nèi)存泄露(增長)分析方法之前,我們先了解一些相關(guān)的基礎(chǔ)概念。 內(nèi)存泄露包括內(nèi)核內(nèi)存泄露、應(yīng)用程序內(nèi)存泄露兩大類。內(nèi)核內(nèi)存泄露可以通過 kmemleak 進(jìn)行檢測,本文主要關(guān)注應(yīng)用程序的內(nèi)存泄露。應(yīng)用程序的內(nèi)存泄露又可以細(xì)分為:堆內(nèi)存(Heap)泄露、內(nèi)存映射區(qū)(Memory Mappings)泄露。我們平時提及的內(nèi)存泄露,主要是指物理內(nèi)存的泄露(持續(xù)分配、映射實際的物理內(nèi)存,且一直未釋放),危害較大,需要立即修復(fù)。 另外,虛擬內(nèi)存的泄露(持續(xù)分配虛擬內(nèi)存,但未分配、映射實際的物理內(nèi)存)容易被忽視,雖然危害相對較小,但也需額外關(guān)注(進(jìn)程的內(nèi)存映射區(qū)總數(shù)量有上限,默認(rèn) 1w)。 通常,應(yīng)用程序內(nèi)存分配涉及的步驟大致如下圖所示:第一,應(yīng)用程序通過內(nèi)存分配器(例如 libc)提供的 malloc 及其變體函數(shù)申請內(nèi)存,free 函數(shù)釋放相應(yīng)內(nèi)存。第二,內(nèi)存分配器(例如 libc)內(nèi)部通過系統(tǒng)調(diào)用 brk 擴展堆內(nèi)存(小塊內(nèi)存分配)。第三,內(nèi)存分配器(例如 libc)內(nèi)部通過系統(tǒng)調(diào)用 mmap 分配內(nèi)存映射區(qū)域(大塊內(nèi)存分配,默認(rèn)不小于 128 KB)第四,二或三已申請的虛擬內(nèi)存在首次寫入時觸發(fā)缺頁異常,OS 分配實際物理頁面,并將虛擬內(nèi)存與其相關(guān)聯(lián),記錄至頁表。 其中,步驟一至三均為虛擬內(nèi)存,步驟四分配實際物理內(nèi)存并創(chuàng)建相應(yīng)頁表。
傳統(tǒng)分析工具 gdb、Valgrind
在定位 mysql-proxy 內(nèi)存泄露(增長)問題的過程中,開發(fā)人員嘗試使用了 Valgrind Memcheck、gdb 進(jìn)行協(xié)助分析。最終前者實際效果不太理想;我通過后者分析出泄露原因,但整個過程耗費了較多時間。 gdb 是常用的程序調(diào)試工具,好處不用贅述。但對于內(nèi)存泄露或增長問題,gdb 缺點也較為明顯,大致如下:干擾程序正常運行,不適合生產(chǎn)環(huán)境;直接定位比較困難,且要求對源碼有一定了解。 Valgrind Memcheck 是一款知名度較高的內(nèi)存泄露分析工具,非常強大,開發(fā)調(diào)試過程中能夠快速發(fā)現(xiàn)場景的內(nèi)存泄露問題。不過開發(fā)者在使用之前,建議對以下情況有所了解:第一,需要重啟程序,且作為 Valgrind 子進(jìn)程運行。不適合分析正在發(fā)生內(nèi)存增長的進(jìn)程。第二,替代默認(rèn)的 malloc/free 等分配函數(shù),目標(biāo)進(jìn)程運行速度減慢 20~30 倍。第三,不能很好的支持 tcmalloc、jemalloc 內(nèi)存分配器。(mysql-proxy 采用了 jemalloc 內(nèi)存分配器)基于動態(tài)追蹤的通用分析方法
對于正在運行、內(nèi)存持續(xù)增長的應(yīng)用來說,gdb、Valgrind Memcheck 工具其實都挺難發(fā)揮價值。相比而言,動態(tài)追蹤技術(shù)提供了一種通用且易用的方式。內(nèi)存分配器相關(guān)函數(shù)調(diào)用、系統(tǒng)調(diào)用、缺頁異常等,都可以看作一個個事件。通過對這些事件的追蹤、統(tǒng)計等,我們可以分析有關(guān)內(nèi)存使用情況的具體代碼路徑,在不深入源碼細(xì)節(jié)的前提下快速縮小泄露發(fā)生的范圍。 本文涉及兩種基于動態(tài)追蹤的通用分析方法:內(nèi)存分配器行為分析、缺頁異常事件分析,涵蓋應(yīng)用程序內(nèi)存分配的常見過程。1)內(nèi)存分配器行為分析
內(nèi)存分配器(glibc、jemalloc 等)行為分析整體思路如下:首先,站在應(yīng)用視角,重點關(guān)注應(yīng)用程序內(nèi)存分配的代碼路徑。其次,動態(tài)追蹤內(nèi)存分配相關(guān)函數(shù),統(tǒng)計未釋放內(nèi)存分配的調(diào)用棧與總字節(jié)數(shù)量,形成分析工具 memstacks。-
開發(fā)新工具 memstacks
-
全量內(nèi)存分配火焰圖
# 步驟 1. 追蹤 60s,生成全量內(nèi)存分配折疊棧
# 其中,參數(shù) -a 表示追蹤所有的 malloc 及其變體,但不追蹤 free 進(jìn)行相互抵消。參數(shù) -f 表示生成折疊棧,用于步驟 2 生成火焰圖。
./memstacks -p $(pgrep -nx mysql-proxy) -af 60 > all_mallocs.stacks
# 步驟 2. 執(zhí)行下述命令生成全量內(nèi)存分配火焰圖,輸出至文件 all_mallocs.svg。
./flamegraph.pl --color=mem --title="All malloc() bytes Flame Graph" --countname="bytes" < all_mallocs.stacks > all_mallocs.svg
火焰圖如下所示,可以協(xié)助開發(fā)者理解 mysql-proxy 調(diào)用 malloc 及其變體的關(guān)鍵代碼路徑。
-
未釋放內(nèi)存分配火焰圖
# 步驟 1. 追蹤 60s,生成未釋放內(nèi)存分配折疊棧
# 其中,參數(shù) -f 表示生成折疊棧,用于步驟 2 生成火焰圖。
memstacks -p $(pgrep -nx mysql-proxy) -f 60 > unfreed_mallocs.stacks
# 步驟 2. 執(zhí)行下述命令生成未釋放內(nèi)存分配火焰圖,輸出到文件 unfreed_mallocs.svg。
./flamegraph.pl --color=mem --title="Unfreed malloc() bytes Flame Graph" --countname="bytes" < unfreed_mallocs.stacks > unfreed_mallocs.svg
火焰圖如下所示,其中:未釋放內(nèi)存共計 27.75 MB(追蹤期間,通過 pidstat 觀察到 mysql-proxy 進(jìn)程 RSS 增量接近 27 MB,與未釋放內(nèi)存統(tǒng)計量 27.75 MB 基本一致)。
已分配但未釋放的代碼路徑主要有兩處。其中,據(jù)研發(fā)反饋,tdsql::set_str 正是導(dǎo)致 mysql-proxy 內(nèi)存泄露發(fā)生的地方。而另一處并非真正的泄露。該工具有一定的副作用,由于追蹤的最后階段有一些剛分配的內(nèi)存還未來得及釋放,需要進(jìn)一步閱讀源碼甄別。另外,建議多運行幾次對比下結(jié)果,排除那些經(jīng)常變化的分配路徑。
對已分配但未釋放的代碼路徑展開,結(jié)果如下:?相比全量內(nèi)存分配火焰圖,數(shù)據(jù)量減少近 60 倍,需要重點關(guān)注的代碼路徑的減少也比較明顯。因此,推薦優(yōu)先使用未釋放內(nèi)存分配火焰圖進(jìn)行分析。
2)缺頁異常事件分析
相比內(nèi)存分配器行為分析,缺頁異常事件分析提供了另一種視角,整體思路如下:首先,站在內(nèi)核視角,關(guān)注的是首次寫入觸發(fā)缺頁異常的代碼路徑,而不是觸發(fā)內(nèi)存分配的代碼路徑。前者是進(jìn)程 RSS增長的原因,后者僅分配了虛擬內(nèi)存,尚未映射物理內(nèi)存。其次,追蹤缺頁異常事件,統(tǒng)計未釋放物理內(nèi)存的調(diào)用棧與總頁面數(shù)量,形成分析工具 pgfaultstacks。-
現(xiàn)有分析工具
perfrecord-p$(pgrep-nxmysql-proxy)-epage-faults-c1-g--sleep60
BCC 工具 stackcount基于靜態(tài)追蹤點 exceptions:page_fault_user。
stackcount -p $(pgrep -nx mysql-proxy) -U tpage_fault_user
現(xiàn)有分析工具雖然方便,但是以增量的方式去統(tǒng)計,不考慮追蹤過程中被釋放的物理內(nèi)存,最終統(tǒng)計的結(jié)果通常會偏大,對內(nèi)存泄露(增長)的分析會造成干擾。
-
缺頁異?;鹧鎴D(現(xiàn)有版)
perf record -p $(pgrep -nx mysql-proxy) -e page-faults -c 1 -g -- sleep 60 > pgfault.stacks
./flamegraph.pl --color=mem --title="Page Fault Flame Graph" --countname="pages" < pgfault.stacks > pgfault.svg
火焰圖具體如下,共計 420,342 次缺頁事件,但不是每一次缺頁事件都分配一個新的物理頁面(大多數(shù)情況下未分配),mysql-proxy RSS 實際增長量僅 60 多MB 。
-
開發(fā)新工具 pgfaultstacks
-
缺頁異?;鹧鎴D
# 步驟 1. 追蹤 60s,生成缺頁異常折疊棧。其中,參數(shù) -f 表示生成折疊棧,用于步驟 2 生成火焰圖。
pgfaultstacks -p $(pgrep -nx mysql-proxy) -f 60 > pgfault.stacks
# 步驟 2. 生成缺頁火焰圖,輸出到文件 pgfault.svg。
./flamegraph.pl --color=mem --title="Page Fault Flame Graph" --countname="pages" < pgfault.stacks > pgfault.svg
缺頁火焰圖如下,其中:共計增加 17801 個物理頁面(與 mysql-proxy 進(jìn)程 RSS 增量基本一致)。重點關(guān)注函數(shù) g_string_append_printf。(注:非內(nèi)存泄露發(fā)生的環(huán)境,僅用來演示缺頁異常火焰圖)
相比現(xiàn)有版,該版本的數(shù)據(jù)量減少 20 多倍,需要重點關(guān)注的代碼路徑減少也比較明顯。
總結(jié)
本文以 TDSQL 實際生產(chǎn)中 mysql-proxy 內(nèi)存泄露問題作為分析對象,探索基于動態(tài)追蹤技術(shù)的通用內(nèi)存泄露(增長)分析方法:內(nèi)存分配器行為分析、缺頁異常事件分析,并針對現(xiàn)有分析工具進(jìn)行改進(jìn),形成相應(yīng)的分析工具 memstacks、pgfaultstacks,歡迎各位開發(fā)者嘗試去開發(fā)。工具使用者僅需關(guān)注少數(shù)可能導(dǎo)致內(nèi)存泄露的代碼路徑,有效提升定位內(nèi)存泄露(增長)問題的效率。如果你正在遭受內(nèi)存泄露(增加)的困擾,不妨嘗試下本文提及的分析方法和工具,希望有所幫助。 審核編輯 :李倩
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。
舉報投訴
-
內(nèi)存
+關(guān)注
關(guān)注
8文章
3029瀏覽量
74103 -
代碼
+關(guān)注
關(guān)注
30文章
4791瀏覽量
68694 -
應(yīng)用程序
+關(guān)注
關(guān)注
37文章
3277瀏覽量
57737
原文標(biāo)題:邢孟棒:2個壓箱底的方法和工具搞定內(nèi)存泄漏
文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
自動駕駛中常提的魯棒性是個啥?
隨著自動駕駛技術(shù)的快速發(fā)展,魯棒性(Robustness)成為評價自動駕駛系統(tǒng)的重要指標(biāo)之一。很多小伙伴也會在自動駕駛相關(guān)的介紹中,對某些功能用魯棒性進(jìn)行描述。一個魯棒的系統(tǒng)能夠在復(fù)雜
免費泄漏率計算工具,讓氣密性檢測變得簡單高效
在工業(yè)生產(chǎn)中,泄漏率是衡量產(chǎn)品氣密性能的重要指標(biāo),它如同「健康體檢報告」,精準(zhǔn)反映設(shè)備的密封狀態(tài),及時預(yù)警潛在的安全隱患與質(zhì)量風(fēng)險。一個微小的泄漏,可能導(dǎo)致能源浪費、安全事故甚至環(huán)境污染。因此,掌握
虛擬內(nèi)存溢出該怎么處理 虛擬內(nèi)存在服務(wù)器中的應(yīng)用
、虛擬內(nèi)存溢出的原因 內(nèi)存泄漏 :程序中未正確釋放的內(nèi)存會導(dǎo)致內(nèi)存泄漏,隨著時間的推移,這些
魯棒性分析方法及其應(yīng)用
魯棒性(Robustness)是指系統(tǒng)或方法對于外部干擾、誤差或變化的穩(wěn)定性和適應(yīng)能力。以下是對魯棒性分析方法的詳細(xì)介紹,以及其在不同領(lǐng)域的應(yīng)用實例。 一、魯
開關(guān)電源輻射老是超?教你一個好方法搞定它
開關(guān)電源輻射老是超?教你一個好方法搞定它【樣機介紹】我本次調(diào)試的樣機主控IC為思睿達(dá)主推的成都啟臣微的CR52168BSG,此IC內(nèi)封了一顆700V的三極管,主要優(yōu)勢為價格低廉、外圍簡單,同時支持
堆棧和內(nèi)存的基本知識
本文主要聊聊關(guān)于堆棧的內(nèi)容。包括堆棧和內(nèi)存的基本知識。常見和堆棧相關(guān)的 bug,如棧溢出,內(nèi)存泄漏,堆內(nèi)存分配失敗等。后面介紹軟件中堆棧統(tǒng)計的重要性,以及如何使用
不到10塊錢,用Ai-M61-32S如何自制一個開機棒?
本作品由安信可社區(qū)用戶 Yhue 制作 當(dāng)你出門在外時,領(lǐng)導(dǎo)一個電話打來需要資料,這時候需要同事去幫忙開機查找,其實只要一根可以遠(yuǎn)程開機電腦的“小棒子”,輕松搞定這一切。究竟是什么棒子這般厲害呢
如何檢測內(nèi)存泄漏
檢測內(nèi)存泄漏是軟件開發(fā)過程中一項至關(guān)重要的任務(wù),它有助于識別和解決那些導(dǎo)致程序占用過多內(nèi)存資源,從而影響程序性能甚至導(dǎo)致程序崩潰的問題。以下將詳細(xì)闡述幾種常見的內(nèi)存
包裝泄漏性檢測方法—真空衰減法
MLT系列微泄漏無損密封測試儀依據(jù)《ASTM F2338-2013 包裝泄漏的標(biāo)準(zhǔn)檢測方法-真空衰減法》標(biāo)準(zhǔn)研發(fā)。適用于預(yù)充式 注射器、水針及粉針瓶(玻璃/塑料)、灌裝壓蓋瓶、奶粉罐、其他硬質(zhì)
NONOS 1.5.3/1.5.4 SSL內(nèi)存泄漏的原因?
我已經(jīng)通過隨附的代碼驗證了當(dāng)發(fā)生 SSL 握手錯誤時,會生成內(nèi)存泄漏
此外,espconn_reconnect_callback不稱為信令ESPCONN_HANDSHAKE - TCP SSL 握手
發(fā)表于 07-18 07:24
使用system_show_malloc()檢查內(nèi)存泄漏遇到異常怎么解決?
我想使用system_show_malloc()檢查內(nèi)存泄漏,但是當(dāng)我調(diào)用該函數(shù)時,我得到了致命的異常:
致命異常 28 (LoadProhibitedCause):
epc1
發(fā)表于 07-10 06:32
如何正確連接和使用泰克電流探棒
在電子工程領(lǐng)域,準(zhǔn)確測量電流是診斷電路問題和驗證設(shè)計性能的關(guān)鍵步驟。泰克(Tektronix)電流探棒是一種精密工具,它允許工程師在不中斷電路的情況下測量電流。本文將指導(dǎo)您如何正確連接和使用泰克電流
華為專利公布:內(nèi)存管理方法及相關(guān)設(shè)備
該專利主要講述如何通過特定方法優(yōu)化內(nèi)存管理效率,包括確定N個具有相同虛擬地址但權(quán)限各異的進(jìn)程(N必須為大于或等于2的整數(shù)),并據(jù)此建立特定映射關(guān)系表以及權(quán)限表,每一進(jìn)程均對應(yīng)一
C語言內(nèi)存泄漏問題原理
內(nèi)存泄漏問題只有在使用堆內(nèi)存的時候才會出現(xiàn),棧內(nèi)存不存在內(nèi)存泄漏問題,因為棧
發(fā)表于 03-19 11:38
?531次閱讀
【鴻蒙】webview內(nèi)存泄漏問題的分析報告
1 關(guān)鍵字 webview;內(nèi)存泄漏 2 問題描述 問題現(xiàn)象:在 3.1release 版本和 3.2bete1 版本中,在 RK3568 上使用 etsWeb 和其他瀏覽器時,webview 所占
評論