dump檔案解析之探索.Net的記憶體
前言:
對於需要長時間執行的.net程式,有時需要我們檢視記憶體的使用有沒有記憶體洩露問題。
我們可以從dump檔案中找到答案。
Dump的看點
用dump檔案來分析記憶體,到底我們需要關心哪些點呢?
- 記憶體的使用情況 HeapSize/object的數量 也就是託管堆使用大小以及託管堆內有多少數量的物件
1.1 檢視有沒有存在有佔用大量記憶體的物件 <比如有某類下面的一個集合>
1.2 0 1 2各代的size<檢視各代的記憶體是否有異常>
2.調查是否有記憶體洩露(重點)
2.1 檢視object的根(Root) 看看GC回收不了的有哪些
2.2<我們知道一個物件Root下沒有引用就會標為可Gc物件,如果一個物件你希望被gc回收但寫程式碼不注意又在別的地方儲存了引用就會出現記憶體洩露>
3. 終結器是否被阻塞時,當終結器執行緒被阻塞時,Finalize會等待累積(末尾有例子)
用什麼工具
- Visual Studio
- DebugDiag
- WinDbg
- dotMemory(JetBrains旗下的 我還沒研究過)
以上三款是微軟給我們提供的工具,注意VS得要是Enterprise才可以哦。其他的兩款都是免費的。
我們先寫一個sample程式
然後執行
一.用Visual Studio
開啟dump檔案
點選按鈕 【除錯託管記憶體】
可以很清楚的看到有多少物件,每個物件共使用了多少記憶體
在這個一覽下方有2個檢視 分別是
1.根的路徑
比如我們選擇 ConsoleApp2.B 這個物件
從這個圖可以看出來 B 這個對應 的 Paths To Root的追溯情況 (也就是構建最終要GC的Root)
Program._values(static變數) -> List<A> -> B
我們可以看到values就是B的Root 只要values不存在那B就會納入gc的回收物件中
因為我們是在Hold住了這個程式的main方法所以在這個時候B 物件還不能被gc回收
2.引用的型別
如何我們選擇List<ConsoleApp3.A>
那麼就會展示List<ConsoleApp3.A>的引用關係如下圖所示:
從這個圖我們可以看出來 List<A> 持有 A[] ,A[]持有 A,A持有B
以上根據這2個檢視我們可以利用Vs來看出:
咦?這個物件佔用記憶體怎麼這麼大 有點可疑
這個物件不是應該被gc嗎,怎麼沒有被gc呢?研究下他的gc root看看
二.DebugDiag
下載地址 https://www.microsoft.com/en-us/download/details.aspx?id=49924
點選 【Add Data Files】 新增Dump檔案後 點選 【Start Analysis】 執行
執行成功後會自動用 IE 開啟。
其實和 VS比起來差不多,直接生成一個報告也是比較方便的!
三.WinDbg
雖然使用上比較麻煩但是winDbg可以幫助我們分析的更加詳細
可以從微軟官方下載,為了方便百度雲下載地址:
連結: https://pan.baidu.com/s/1eblPm4nuN0F-DkY_FzqUvA 提取碼: zmtd
注意要設定下Symbol Path
重新設定符號檔案路徑如下;
SRV*C:\Symbols*http://msdl.microsoft.com/download/symbols
意思是如果在本地找不到則從微軟網站下載
Ok設定完成後用WinDbg載入dump檔案
如下圖所示:
開啟成功後我們還不能開始分析必須
要先執行載入SOS和CLR(對於.Net Runtime 4.0)
【 !loadby sos clr】 命令
接下來我們用WinDbg來調查記憶體使用情況:
一般我們定位記憶體洩露問題,我總結的原則是要查到什麼物件佔用了大量記憶體,為什麼它沒有被GC。我們分以下幾個步驟
1.統計記憶體中的物件&查詢有異樣的物件
使用命令 【!DumpHeap -stat】
可以把堆中的物件型別和size給列印出來
我們可以看出來 A 和 B 是可疑物件
2.根據型別查詢存活物件一覽
接下來我們根據查詢出A的一覽
使用命令【!DumpHeap -live -mt <MT addr>】
結果如下:
可以看出記憶體中A型別的物件有100000個
3.探索從某個物件找出GC ROOT一覽
使用命令【!GCRoot 】
其實可以看出來和Visual Studio的【根的路徑】要點差不多吧。
WinDbg的其他常用命令
1. !DumpHeap -stat 查詢託管堆按型別分組統計個數以及佔用的總記憶體大小
2.!HeapStat 查詢當前堆中各代的記憶體使用量 以及剩餘使用量
3.!DumpHeap -live -mt <MT addr> 從MethodTable中查詢存活的物件一覽
4.!DumpHeap -dead -mt <MT addr> 從MethodTable中查詢要在下次FullGC要回收的一覽
5.!DumpMT -md <MT addr> 檢視型別資訊 (加了-md引數會把這個型別下的方法(MethodDescriptor)都列印出來)
6.!DumpClass <EEClass addr> 指定EEClass的地址
7.!Threads 檢視Finalizer有沒有導致死鎖的例子
如上圖所示, 用!Threads可以找出 Finalizer的執行緒為13.。接下來用命令 ~13k 檢視執行緒執行棧,
上圖是正常的情況沒有問題。執行WaitForFinalizerEvent等下一次執行訊號
下圖是死鎖情況
可以看到有 CallFinalizer 和 FinalizeAllObjects 表示正在處理什麼東西
在用命令【~[ID]e!clrstack】檢視CLR的執行棧情況
可以看出是在Finalize裡面用了 Thread.Sleep導致的
使用SOSEX更方便的使用WinDbg
sosez是sos的擴充套件工具集(就是一個dll檔案),下載官網地址:http://www.stevestechspot.com/
下載完後要載入到 WinDbg裡面去 使用命令 .load 進行載入
它整合了很多簡單使用的指令
例如: !mdt 可以根據 型別 進行篩選
藍色處可以直接點選檢視,功能很強大
總結
.Net程式執行期間會遇到很多奇怪的問題,通過分許Dump檔案分析記憶體情況是一個很好的切入口。
不管用什麼工具,按照以下幾個步驟:
- 統計記憶體中的物件
- 查詢有異樣的物件 比如這個物件數量多的有點反常,本來期待會被GC回收但是卻沒有
- 根據型別查詢存活物件一覽
- 探索從某個物件找出GC ROOT一覽
一定會讓你有所發現。
如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的“推薦”將是我最大的寫作動力!歡迎各位轉載,轉載文章之後須在文章頁面明顯位置給出作者和原文連線,謝謝。
相關文章
- 記記憶體條硬體損壞藍色畫面的 dump 檔案分析記憶體
- 溫故之.NET程式間通訊——記憶體對映檔案記憶體
- 【Redis】 redis解析rdb檔案,記憶體排序等Redis記憶體排序
- .Net程式記憶體洩漏解析記憶體
- docker下netcore記憶體dumpDockerNetCore記憶體
- Visual Studio 快速分析 .NET Dump 檔案
- iOS記憶體深入探索之VM TrackeriOS記憶體
- 溫故之.NET記憶體管理記憶體
- Android記憶體優化(四)解析Memory Monitor、Allocation Tracker和Heap DumpAndroid記憶體優化
- linux檔案存在記憶體裡Linux記憶體
- 探索Java記憶體模型Java記憶體模型
- Windbg下使用dump分析記憶體溢位記憶體溢位
- Ubuntu 新增虛擬記憶體檔案Ubuntu記憶體
- 使用記憶體對映檔案(mmap)記憶體
- 記憶體檔案系統的再學習記憶體
- Python mmap的使用-檔案記憶體對映Python記憶體
- 探索JVM的垃圾回收(堆記憶體)JVM記憶體
- 容器中的 .net core應用,生成dump檔案並匯出
- C#記憶體對映大檔案並使用Marshal解析結構體資訊C#記憶體結構體
- 記一次dump檔案分析歷程
- 作業系統-記憶體、檔案管理作業系統記憶體
- 利用dotnet-dump分析docker容器記憶體洩露Docker記憶體洩露
- 議題解析與復現--《Java記憶體攻擊技術漫談》(二)無檔案落地Agent型記憶體馬Java記憶體
- golang 釋放記憶體機制的探索Golang記憶體
- .NET 記憶體洩漏的爭議記憶體
- 一種基於記憶體的檔案系統tmpfs記憶體
- 二進位制檔案記憶體對映記憶體
- .Net Core中的配置檔案原始碼解析原始碼
- .Net Core 中介軟體之靜態檔案(StaticFiles)
- C++記憶體模型實踐探索C++記憶體模型
- 探索MYSQL開啟大頁記憶體MySql記憶體
- Spring Boot RCE到記憶體馬探索Spring Boot記憶體
- Swift記憶體賦值探索一: 理解物件在記憶體中的儲存狀態Swift記憶體賦值物件
- 關於dataWithContentsOfFile 讀取大檔案的記憶體問題記憶體
- PHP超低記憶體遍歷目錄檔案和讀取超大檔案PHP記憶體
- windows核心程式設計--記憶體對映檔案Windows程式設計記憶體
- 居然這就是C++記憶體對映檔案?!C++記憶體
- 記憶體管理兩部曲之實體記憶體管理記憶體