一:背景 去年 GC架構師 Maoni 在 (2021 .NET 開發者大會) [https://ke.segmentfault.com/course/1650000041122988/section/1500000041123017] 上演示過 PerfView 的 Diff 功能來尋找記憶體增量, ...
一:背景
去年 GC架構師 Maoni
在 (2021 .NET 開發者大會)
[https://ke.segmentfault.com/course/1650000041122988/section/1500000041123017] 上演示過 PerfView 的 Diff 功能來尋找記憶體增量,個人感覺這個功能非常不錯,簡單省事,所以這裡就整合到 PerfView 專題中,分享一下給大家。
二:洞察記憶體增量
1. 什麼是記憶體增量
其實非常好理解,就是當你的程式出現了記憶體泄漏,你可以在程式記憶體增長的過程中截取兩個 dump 文件,然後通過 PerfView 觀察其中的記憶體增量是什麼? 幫助我們快速找出可能被泄漏的對象。
當然你用 WinDBG 的話也是沒有問題的,只不過需要用肉眼掃一下而已,接下來舉兩個例子說明一下。
2. 靜態集合的記憶體泄漏
很多 dump 的記憶體泄漏,源自於裡面的某一個 static 變數無限堆積所致,為了方便說明,先上一段測試代碼。
internal class Program
{
static void Main(string[] args)
{
Task.Run(RunTest);
Console.ReadLine();
}
public static List<string> my_big_list = new List<string>();
static void RunTest()
{
for (int i = 0; i < 50000; i++)
{
my_big_list.Add(string.Join(",", Enumerable.Range(0, 10000)));
Console.WriteLine(i);
}
}
}
接下來在程式的運行過程中,我們分別截取兩個 dump 文件, 點擊菜單欄的 Memory -> Take Heap Snapshot
按鈕,在 Filter
中搜索需要採集的進程,然後點擊 Dump GC Heap
即可,參考如下圖:
稍等片刻,你就會看到兩個 gcdump
文件,這個和普通的 dump
是不一樣的,算是 PerfView 專用的輕量級 dump 文件,截圖如下:
接下來點擊兩個 gcdump 中的 Heap Stacks
,對比 inc%
列後發現,記憶體都被一個叫 my_big_list
變數給吃掉了,前者的count為 10509
, 後者是 15172
,截圖如下:
雖然肉眼可以簡單觀察,但這裡可以使用專業的 Diff
功能,讓 PerfView 幫我洞察 棧 的總體增量差異,點擊菜單欄中的 Diff -> With Baseline: Heap Stacks [.....]
按鈕,即讓本 gcdump
和另一個 gcdump
做比較,截圖如下:
不過要註意的是,這兩個視窗一定要打開,這個是比較坑的,哈哈,接下來就會看到如下圖:
從圖中可以清晰的看到,這兩個 dump 的增量主要來自於 my_big_list
集合,往細處說就是 string 增長了 4663
個。
3. 事件event泄漏
我們再看一個事件泄漏的例子,參考如下代碼:
// event 泄漏
class Program
{
static event Action TestEvent;
static void Main(string[] args)
{
var memory = new TestAction();
//handle 泄漏
for (int i = 0; i < int.MaxValue; i++)
{
TestEvent += memory.Run;
if (i % 500 == 0)
{
Console.WriteLine(i);
}
}
Console.ReadLine();
}
public static void OnTestEvent()
{
if (TestEvent != null)
{
TestEvent();
}
else
{
Console.WriteLine("Test Event is null");
}
}
class TestAction
{
public void Run()
{
Console.WriteLine("TestAction Run.");
}
}
}
將程式運行起來,用 Process Explorer
抓兩個 dump 文件下來,然後點擊 Memory -> Take Heap Snapshot From Dump
按鈕,截圖如下:
在彈出的對話框中設置需要提取的 dump 文件,稍等片刻就會生成如下兩個 gcdump
文件,截圖如下:
接下來將兩個 gcdump 都打開,發現記憶體都被程式中的一個叫 TestEvent
占用了,如下圖所示:
接下來就可以使用 Diff
對比功能了,可以觀察到,TestEvent 下麵的 Action 增量了將近 700w
個,截圖如下:
這裡稍微說一下,為什麼會增量 700w 的 Action,這主要是因為 event 是一個多播委托,內部有一個 Action 集合,也正是這個 Action 集合
在無限膨脹。