日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

记一次 .NET 某HIS系统后端服务 内存泄漏分析

發布時間:2023/12/4 windows 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 记一次 .NET 某HIS系统后端服务 内存泄漏分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一:背景

1. 講故事

前天那位 his 老哥又來找我了,上次因為CPU爆高的問題我給解決了,看樣子對我挺信任的,這次另一個程序又遇到內存泄漏,希望我幫忙診斷下。

其實這位老哥技術還是很不錯的,他既然能給我dump,那真的是遇到很棘手的疑難雜癥了????????????,我得做好心理準備????????????,溝通下來大概就是程序的內存會緩慢膨脹,直到自毀,問題就是這么一個問題,接下來祭出我的看家工具 windbg。

二:windbg 分析

1. 到底哪里泄漏了?

我在之前很多篇文章中都說過,遇到這種內存泄漏,首先就要排查到底是 托管堆 還是 非托管堆 的問題 ?如果是后者,大多數情況只能舉手投降,因為這里面水太深了。。。別看那些案例用 AllocHGlobal 方法分配非托管內存,然后用 !heap 去找的小兒科,現實情況比這種要復雜的多。。。

接下來先用 !address -summary 看一下當前進程的提交內存。

0:000>?!address?-summary---?Usage?Summary?----------------?RgnCount?-----------?Total?Size?--------?%ofBusy?%ofTotal Free????????????????????????????????????345?????7dfd`ca3ca000?(?125.991?TB)???????????98.43% <unknown>?????????????????????????????37399??????201`54dbf000?(???2.005?TB)??99.83%????1.57% Heap??????????????????????????????????29887????????0`d179b000?(???3.273?GB)???0.16%????0.00% Image??????????????????????????????????1312????????0`0861b000?(?134.105?MB)???0.01%????0.00% Stack???????????????????????????????????228????????0`06e40000?(?110.250?MB)???0.01%????0.00% Other????????????????????????????????????10????????0`001d8000?(???1.844?MB)???0.00%????0.00% TEB??????????????????????????????????????76????????0`00098000?(?608.000?kB)???0.00%????0.00% PEB???????????????????????????????????????1????????0`00001000?(???4.000?kB)???0.00%????0.00%---?Type?Summary?(for?busy)?------?RgnCount?-----------?Total?Size?--------?%ofBusy?%ofTotal MEM_MAPPED??????????????????????????????352??????200`00a40000?(???2.000?TB)??99.57%????1.56% MEM_PRIVATE???????????????????????????67249????????2`2cbcb000?(???8.699?GB)???0.42%????0.01% MEM_IMAGE??????????????????????????????1312????????0`0861b000?(?134.105?MB)???0.01%????0.00%---?State?Summary?----------------?RgnCount?-----------?Total?Size?--------?%ofBusy?%ofTotal MEM_FREE????????????????????????????????345?????7dfd`ca3ca000?(?125.991?TB)???????????98.43% MEM_RESERVE???????????????????????????11805??????200`22ae8000?(???2.001?TB)??99.60%????1.56% MEM_COMMIT????????????????????????????57108????????2`1313e000?(???8.298?GB)???0.40%????0.01%

從卦象上看, 進程提交內存 MEM_COMMIT = 8.2G, 然后我們看下托管堆大小,使用 !eeheap -gc 命令。

0:000>?!eeheap?-gc Number?of?GC?Heaps:?1 generation?0?starts?at?0x0000027795928060 generation?1?starts?at?0x000002779572F0D0 generation?2?starts?at?0x000002763DCE1000Total?Size:??????????????Size:?0xcd28c510?(3442001168)?bytes. ------------------------------ GC?Heap?Size:????Size:?0xcd28c510?(3442001168)?bytes.

從最后一行可以看出,當前的GC堆 Size= 3442001168 /1024/1024/1024 =3.2G,也就是說大概:8.2G - 3.2G = 5G 的內存丟掉了。。。尼瑪,典型的 非托管內存泄漏,真的是哪壺不開提哪壺,這下可能真的要栽了。。。

2. 尋找非托管內存泄漏

除了 GC 堆,進程里面還有一個叫做 loader 堆,這里面東西就多了,有高頻堆,低頻堆,Stub堆,JIT堆 等等,存放著和 AppDomain,Module,方法描述符,方法表,EEClass 等相關信息,從經驗來說,這個 loader 堆是考察 非托管泄漏 優先考慮的地方,要想查看,可使用 !eeheap -loader 命令。

0:000>?!eeheap?-loader ... Module?00007ffe2b1b6ca8:?Size:?0x0?(0)?bytes. Module?00007ffe2b1b7e80:?Size:?0x0?(0)?bytes. Module?00007ffe2b1b9058:?Size:?0x0?(0)?bytes. Module?00007ffe2b1ba230:?Size:?0x0?(0)?bytes. Module?00007ffe2b1bb408:?Size:?0x0?(0)?bytes. Module?00007ffe2b1bc280:?Size:?0x0?(0)?bytes. Module?00007ffe2b1bd458:?Size:?0x0?(0)?bytes. Module?00007ffe2b1be630:?Size:?0x0?(0)?bytes. Module?00007ffe2b1bf808:?Size:?0x0?(0)?bytes. Module?00007ffe2b1f0a50:?Size:?0x0?(0)?bytes. Module?00007ffe2b1f1c28:?Size:?0x0?(0)?bytes. Module?00007ffe2b1f2aa0:?Size:?0x0?(0)?bytes. Total?size:??????Size:?0x0?(0)?bytes. -------------------------------------- Total?LoaderHeap?size:???Size:?0xc0fb9000?(3237711872)?bytes?total,?0x5818000?(92372992)?bytes?wasted.

這命令不輸還好,一輸嚇一跳,windbg 界面刷了好幾分鐘才停下來。。。從輸出中可以得到兩點信息:

  • loader堆 總共占用:3237711872 /1024/1024/1024 = 3.01G

  • 有非常多的 module 產生,我估計有幾萬個。。。

為了滿足好奇心,我決定寫一個小腳本看看到底有多少個 module ???

我去,module居然有19w之多,難怪占用了 3 個多G,感覺離真相不遠了,接下來的問題是這些module是什么,從哪里來???

3. 尋找 module 的源頭

要想尋找源頭,大家可以仔細想一想, module 的嵌套關系應該是:Module -> Assembly -> Appdomain ,所以查 AppDomain 或許能給我們更多的信息,接下來使用 !DumpDomain 導出當前進程的所有應用程序域,又是刷刷刷的幾分鐘,哎。。。截圖如下:

從圖中可以看出有大量的 Dynamic 類型的程序集,你肯定想問這是什么意思?對,這就是代碼動態創建的程序集,居然高達 19w 。。。接下來要解決的一個問題是:這些 Assembly 是怎么創建出來的???

4. 導出 module 內容

老讀者應該知道我是怎么從 module 中導出問題代碼的,對,就是尋找 module 的 startaddress,這里我就挑選其中一個module:00007ffe2b1f2aa0。

2:2:152>?!dumpmodule?00007ffe2b1f2aa0 Name:?Unknown?Module Attributes:??????????????Reflection?SupportsUpdateableMethods?IsDynamic?IsInMemory? Assembly:????????????????000002776c1d8470 BaseAddress:?????????????0000000000000000 PEFile:??????????????????000002776C1D8BF0 ModuleId:????????????????00007FFE2B1F2EB8 ModuleIndex:?????????????00000000000177CF LoaderHeap:??????????????0000000000000000 TypeDefToMethodTableMap:?00007FFE2B1EE8C0 TypeRefToMethodTableMap:?00007FFE2B1EE8E8 MethodDefToDescMap:??????00007FFE2B1EE910 FieldDefToDescMap:???????00007FFE2B1EE960 MemberRefToDescMap:??????0000000000000000 FileReferencesMap:???????00007FFE2B1EEA00 AssemblyReferencesMap:???00007FFE2B1EEA28

我去,BaseAddress 居然沒有地址,真倒霉,這也就是說該 module 你是無法導出的,想想也對,畢竟是動態生成的,可能寫代碼的人都搞不清楚module中是什么?難道真的就沒有辦法了嗎?可俗話說得好,天無絕人之路????????????,在 !dumpmodule 命令中有一個 mt (methodtable) 參數,用來顯示當前module中都有哪些類型,這就是重大線索。

||2:2:152>?!dumpmodule?-mt?00007ffe2b1f2aa0? Name:?Unknown?Module Attributes:??????????????Reflection?SupportsUpdateableMethods?IsDynamic?IsInMemory? Assembly:????????????????000002776c1d8470Types?defined?in?this?moduleMT??????????TypeDef?Name ------------------------------------------------------------------------------ 00007ffe2b1f3168?0x02000002?<Unloaded?Type> 00007ffe2b1f2f60?0x02000003?<Unloaded?Type>Types?referenced?in?this?moduleMT????????????TypeRef?Name ------------------------------------------------------------------------------ 00007ffdb9f70af0?0x02000001?System.Object 00007ffdbaed3730?0x02000002?Castle.DynamicProxy.IProxyTargetAccessor 00007ffdbaec8f98?0x02000003?Castle.DynamicProxy.ProxyGenerationOptions 00007ffdbaec7fe8?0x02000004?Castle.DynamicProxy.IInterceptor

可以看到module中定義了兩個 type,都有其方法表地址,接下來通過 mt 來換取 md (方法描述符) 來得到最后module內容。

到這里終于就搞清楚了,原來這位老哥是利用 Castle 做了一個 AOP 的功能,應該是沒有正確的使用 AOP ,導致生成了 19w + 的動態程序集,難怪最終會把內存給弄爆掉。。。根子總算找到了,接下來如何去修改呢???

5. 修改 Castle AOP 問題代碼

這下可把我難住了,畢竟我真的是沒玩過 Castle ????????????,不過老規矩,到 bing 上看看可有 天涯淪落人,嘿嘿,還真有 Castle AOP 導致內存泄漏的文章:Castle Windsor Interceptor memory leak ,解決辦法也提供了,截圖如下:

趕緊把這篇鏈接丟給老哥,我感覺也只能幫他到這里了,剩下的只能看造化。

三:總結

真的是造化弄人,老哥以迅雷不及掩耳之勢就給搞定了,當天晚上就已完成自測上線。

我趕緊追問老哥是怎么改的????????????,老哥也不惜把源碼放出來了,果然按照老外的建議將 ProxyGenerator 設置成 static 就搞定了。。。否則一個new一個assembly,再看看改之前的代碼,截圖如下:

搞定了這兩個難啃的問題,感覺是不是要發一個小獎杯給我呢?????????????

END

工作中的你,是否已遇到 ...?

1. CPU爆高

2. 內存暴漲

3. 資源泄漏

4. 崩潰死鎖

5. 程序呆滯

等緊急事件,全公司都指望著你能解決...? 危難時刻才能展現你的技術價值,作為專注于.NET高級調試的技術博主,歡迎微信搜索: 一線碼農聊技術,免費協助你分析Dump文件,希望我能將你的踩坑經驗分享給更多的人。

總結

以上是生活随笔為你收集整理的记一次 .NET 某HIS系统后端服务 内存泄漏分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。