七月底的時候有位朋友在wx上找到我,說他的程式記憶體占用8G,托管才占用1.5G,詢問剩下的記憶體哪裡去了?截圖如下:
從求助内容看,這位朋友真的太客氣了,動不動就談錢,真傷感情,如果有朋友一直關注我的分享,應該知道我一直都是免費分析dump,當然我的知識和經驗也是有邊界的,有些dump我也搞不定,不過我還是盡自己最大努力去尋找答案。
在這裡我有必要說一下職場,在我的潛意識或者在我的團隊中,這些很難搞的問題當然由技術上司去搞定,但我發現有好幾起卻不是這樣的,技術經理搞不定轉包下來,下面搞不定就讓他另請高明。。。😥😥😥 有大佬可以分析下嗎。
好了,閑話不多說,當務之急上windbg說話。
我在很多分析記憶體洩漏方面的文章都提到過,先要用二分法确定下是哪一部分的記憶體洩漏(托管還是非托管)。
從 <code>!address -summary</code> 和 <code>!eeheap -gc</code> 兩條指令看,确實如朋友所說:<code>MEM_COMMIT=8.3G, GC Heap=1.5G</code>, 我去,果然是難搞的非托管記憶體洩漏,既然是地獄模式,那就硬着頭皮繼續看吧,要想繼續排查的話,首先得看 <code>windows nt</code> 堆。
其實不管是托管的C#還是非托管的C,C++,它們配置設定記憶體最終都需要調用 Windows 的 <code>VirtualAlloc,HeapAlloc</code> API 到 windows nt 上,接下來的研究方向是如何查找這些 .net 看不到的 nt堆, 可以使用 windbg 的 <code>!heap -s</code> 指令。
從上面的資訊可以看出,目前有 14個 heap,其中最大的一個heap占了 <code>4.8G</code>,為啥這個heap這麼大? 接下來詳細看下這個heap,可使用 <code>!ext.heap -stat -h 000001bacd190000</code> 。
可能很多人看不懂上面的卦象,首先 <code>busy</code>表示那些最近配置設定還未釋放的,從卦頭看,size=20034 的 block 有 36590 個,總占用:<code>11df90858 = 4797827160byte = 4.7G</code>,接下來的疑問很顯然了,這些 block 裡面到底都是些什麼??? 要想找到答案,把這 <code>3w</code> 多的 block 資訊都顯示出來,可以用指令: <code>!ext.heap -flt s 20034</code>。
block塊資訊太多,這裡我就貼一部分上去,上面列的 HEAP_ENTRY 就是 block 的首位址,然後我通過 <code>dc</code> 一頓找,發現不少下面的輸出。
說實話用dc一個一個找,真的太累,這裡我就寫一個簡單的腳本,把前1w個block都dc出來看看内容咋樣?
腳本執行後,輸出結果如下:
問了下朋友這些字元串大概是幹嘛的? 為啥非托管中有這麼多的string沒有得到釋放,朋友告訴我這個大概是門禁相關業務,是通過 plc 方式和 C# 進行互動,分析到這裡我能提供的資訊都已提供了,接下來就要和門禁業務方确認下如何進一步定位和改進了。
貌似這是20篇dump案例分享中第一個聊到非托管洩露的問題,曾今我在B站上說隻專注于分析.NET托管記憶體洩漏,看樣子很難實作哈,确實 C# 和 lua,C++,COM,内嵌浏覽器 的互動造成非托管記憶體洩漏的例子數不勝數哈 😂😂😂
更多高品質幹貨:參見我的 GitHub: dotnetfly