C# dump文件解析之探索.NET的內(nèi)存
當(dāng)前位置:點(diǎn)晴教程→知識(shí)管理交流
→『 技術(shù)文檔交流 』
前言:對(duì)于需要長(zhǎng)時(shí)間運(yùn)行的.net程序,有時(shí)需要我們查看內(nèi)存的使用有沒有內(nèi)存泄露問題。 我們可以從dump文件中找到答案。 Dump的看點(diǎn)用dump文件來分析內(nèi)存,到底我們需要關(guān)心哪些點(diǎn)呢?
1.1 查看有沒有存在有占用大量?jī)?nèi)存的對(duì)象 <比如有某類下面的一個(gè)集合> 1.2 0 1 2各代的size<查看各代的內(nèi)存是否有異常>
2.調(diào)查是否有內(nèi)存泄露(重點(diǎn)) 2.1 查看object的根(Root) 看看GC回收不了的有哪些 2.2<我們知道一個(gè)對(duì)象Root下沒有引用就會(huì)標(biāo)為可Gc對(duì)象,如果一個(gè)對(duì)象你希望被gc回收但寫代碼不注意又在別的地方保存了引用就會(huì)出現(xiàn)內(nèi)存泄露> 3. 終結(jié)器是否被阻塞時(shí),當(dāng)終結(jié)器線程被阻塞時(shí),F(xiàn)inalize會(huì)等待累積(末尾有例子)
用什么工具
以上三款是微軟給我們提供的工具,注意VS得要是Enterprise才可以哦。其他的兩款都是免費(fèi)的。 我們先寫一個(gè)sample程序
然后運(yùn)行 一.用Visual Studio打開dump文件 點(diǎn)擊按鈕 【調(diào)試托管內(nèi)存】 可以很清楚的看到有多少對(duì)象,每個(gè)對(duì)象共使用了多少內(nèi)存 在這個(gè)一覽下方有2個(gè)視圖 分別是 1.根的路徑 比如我們選擇 ConsoleApp2.B 這個(gè)對(duì)象 從這個(gè)圖可以看出來 B 這個(gè)對(duì)應(yīng) 的 Paths To Root的追溯情況 (也就是構(gòu)建最終要GC的Root) Program._values(static變量) -> List<A> -> B 我們可以看到values就是B的Root 只要values不存在那B就會(huì)納入gc的回收對(duì)象中 因?yàn)槲覀兪窃贖old住了這個(gè)程序的main方法所以在這個(gè)時(shí)候B 對(duì)象還不能被gc回收 2.引用的類型 如何我們選擇List<ConsoleApp3.A> 那么就會(huì)展示List<ConsoleApp3.A>的引用關(guān)系如下圖所示: 從這個(gè)圖我們可以看出來 List<A> 持有 A[] ,A[]持有 A,A持有B 以上根據(jù)這2個(gè)視圖我們可以利用Vs來看出: 咦?這個(gè)對(duì)象占用內(nèi)存怎么這么大 有點(diǎn)可疑 這個(gè)對(duì)象不是應(yīng)該被gc嗎,怎么沒有被gc呢?研究下他的gc root看看
二.DebugDiag下載地址 https://www.microsoft.com/en-us/download/details.aspx?id=49924 點(diǎn)擊 【Add Data Files】 添加Dump文件后 點(diǎn)擊 【Start Analysis】 執(zhí)行 執(zhí)行成功后會(huì)自動(dòng)用 IE 打開。 其實(shí)和 VS比起來差不多,直接生成一個(gè)報(bào)告也是比較方便的! 三.WinDbg雖然使用上比較麻煩但是winDbg可以幫助我們分析的更加詳細(xì) 可以從微軟官方下載,為了方便百度云下載地址: 鏈接: https://pan.baidu.com/s/1eblPm4nuN0F-DkY_FzqUvA 提取碼: zmtd 注意要設(shè)置下Symbol Path 重新設(shè)置符號(hào)文件路徑如下; SRV*C:\Symbols*http://msdl.microsoft.com/download/symbols 意思是如果在本地找不到則從微軟網(wǎng)站下載 Ok設(shè)置完成后用WinDbg載入dump文件 如下圖所示: 打開成功后我們還不能開始分析必須 要先執(zhí)行加載SOS和CLR(對(duì)于.Net Runtime 4.0) 【 !loadby sos clr】 命令
接下來我們用WinDbg來調(diào)查內(nèi)存使用情況: 一般我們定位內(nèi)存泄露問題,我總結(jié)的原則是要查到什么對(duì)象占用了大量?jī)?nèi)存,為什么它沒有被GC。我們分以下幾個(gè)步驟 1.統(tǒng)計(jì)內(nèi)存中的對(duì)象&查找有異樣的對(duì)象 使用命令 【!DumpHeap -stat】 可以把堆中的對(duì)象類型和size給打印出來 我們可以看出來 A 和 B 是可疑對(duì)象 2.根據(jù)類型查找存活對(duì)象一覽接下來我們根據(jù)查找出A的一覽 使用命令【!DumpHeap -live -mt <MT addr>】 結(jié)果如下: 可以看出內(nèi)存中A類型的對(duì)象有100000個(gè) 3.探索從某個(gè)對(duì)象找出GC ROOT一覽使用命令【!GCRoot 】
其實(shí)可以看出來和Visual Studio的【根的路徑】要點(diǎn)差不多吧。
WinDbg的其他常用命令1. !DumpHeap -stat 查找托管堆按類型分組統(tǒng)計(jì)個(gè)數(shù)以及占用的總內(nèi)存大小
2.!HeapStat 查找當(dāng)前堆中各代的內(nèi)存使用量 以及剩余使用量
3.!DumpHeap -live -mt <MT addr> 從MethodTable中查找存活的對(duì)象一覽
4.!DumpHeap -dead -mt <MT addr> 從MethodTable中查找要在下次FullGC要回收的一覽
5.!DumpMT -md <MT addr> 查看類型信息 (加了-md參數(shù)會(huì)把這個(gè)類型下的方法(MethodDescriptor)都打印出來)
6.!DumpClass <EEClass addr> 指定EEClass的地址
7.!Threads 查看Finalizer有沒有導(dǎo)致死鎖的例子
如上圖所示, 用!Threads可以找出 Finalizer的線程為13.。接下來用命令 ~13k 查看線程執(zhí)行棧,
上圖是正常的情況沒有問題。執(zhí)行WaitForFinalizerEvent等下一次執(zhí)行信號(hào)
下圖是死鎖情況 可以看到有 CallFinalizer 和 FinalizeAllObjects 表示正在處理什么東西 在用命令【~[ID]e!clrstack】查看CLR的執(zhí)行棧情況 可以看出是在Finalize里面用了 Thread.Sleep導(dǎo)致的
使用SOSEX更方便的使用WinDbgsosez是sos的擴(kuò)展工具集(就是一個(gè)dll文件),下載官網(wǎng)地址:http://www.stevestechspot.com/ 下載完后要加載到 WinDbg里面去 使用命令 .load 進(jìn)行加載 它集成了很多簡(jiǎn)單使用的指令 例如: !mdt 可以根據(jù) 類型 進(jìn)行篩選
藍(lán)色處可以直接點(diǎn)擊查看,功能很強(qiáng)大
總結(jié).Net程序運(yùn)行期間會(huì)遇到很多奇怪的問題,通過分許Dump文件分析內(nèi)存情況是一個(gè)很好的切入口。 不管用什么工具,按照以下幾個(gè)步驟:
轉(zhuǎn)自https://www.cnblogs.com/yudongdong/p/9701727.html 該文章在 2024/11/19 9:17:28 編輯過 |
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |