前幾天,有位朋友問我,你平時都是怎么去排查一個程式的性能問題的啊,不要誤會,這位朋友不是我啦,因為我真的有這樣一位叫做 Toby 的朋友,說到性能問題,可能大家立馬會想到類似并發數、吞吐量、回應時間、QPS、TPS等等這些指標,這些指標的確可以反映出一個系統性能的好壞,可隨著我們的系統結構變得越來越復雜,要找到這樣一個性能的“損耗點”,同樣會變得越來越困難,在不同的人的眼中,對于性能好壞的評判標準是不一樣的,譬如在前端眼中,頁面打開速度的快慢代表著性能的好壞;而在后端眼中,并發數、吞吐量和回應時間代表著性能的好壞;而在 DBA 眼中,一條 SQL 陳述句的執行效率代表著性能的好壞,更不用說,現實世界中的程式要在硬體、網路的世界里來回穿梭了,所以,從80%的功能堆積到100%,是件非常容易的事情;而從80%的性能優化到85%,則不是件非常輕松的事情,想清楚這一點非常簡單,因為我們的系統從來都不是簡單的1 + 1 = 2,此時,我們需要一個性能分析工具,而今天給大家分享的是 JetBrains 出品的 dotTrace ,
快速開始(Quick Start)
安裝軟體的程序此處不表,這里建議大家同時安裝 dotTrace 和 dotMemery,因為這都是 JetBrains 全家桶中的軟體,安裝的時候選一下就可以了,可謂是舉手之勞,安裝好以后的界面是這樣的,可以注意到,它可以對行程中的 .NET 應用、本機的 .NET 應用以及遠程的 .NET 應用進行檢測,因為這里寫一個 .NET Core 應用來作為演示,所以,我們選擇 Profile Local App:

在這里,我們準備了一個簡單的控制臺程式:
public class Program
{
static void Main(string[] args)
{
CPUHack();
MemeryHack();
}
public static void MemeryHack() {
Console.ReadLine();
var bytes = GC.GetTotalAllocatedBytes();
Console.WriteLine($"AllocatedBytes: { bytes } bytes");
var list = new List<byte[]>();
try
{
while (true) {
list.Add(new byte[85000]);
}
} catch (OutOfMemoryException) {
Console.WriteLine(nameof(OutOfMemoryException));
Console.WriteLine(list.Count);
bytes = GC.GetTotalAllocatedBytes();
Console.WriteLine($"AllocatedBytes: { bytes } bytes");
}
Console.ReadLine();
}
public static void CPUHack() {
Parallel.For(0, Environment.ProcessorCount,
new ParallelOptions() {
MaxDegreeOfParallelism = Environment.ProcessorCount
},
i => {
});
}
}
其中,CPUHack()方法來自:打爆你的 CPU; MemeryHack()方法來自:通過代碼實作 OutOfMemory,顧名思義,我們將利用這兩個方法來分別測驗 dotTrace 和 dotMemery,
dotTrace 目前支持以下平臺:.NET、.NET Core、WPF、UWP(Universal Windows Platform)、ASP.NET、Windows服務、WCF、Mono 和 Unity,可以注意到它有四種監測方式,即Sampling、Tracing、Line by Line以及Timeline,按照界面上的描述,Sampling 適用于大多數場景下呼叫時間的精確測量、Tracing 適用于演算法復雜度分析場景下呼叫次數的精確測量、Line by Line 適用于更高級別的使用場景,Timeline 適用于含多執行緒在內的資料處理的精確測量,所以,我們這里選擇好一個可執行檔案,然后選擇 Sampling,再點擊 “Run”:

此時,我們會看到對應程式的的工具列,我們可以點擊 “Get Snapshot and Wait” 進行采樣,每次采樣會生成一個快照,默認情況下會自動打開生成的快照,我們還可以點擊 “Start” 重新進行采樣,直至采集到滿意的樣本為止,而在完成采樣后,則可以點擊 “Kill” 結束采樣,下面來看看生成的快照:

通過這兩圖,我們可以非常清晰的看到,最耗時的正是我們這里的CPUHack()方法,并且這里一共有四個執行緒,這是因為博主的計算機使用的是一款4核的i3處理器,并且在dotTrace中可以直接看到相關的代碼片段,當然,這一切的前提是你沒有對應用程式做過混淆處理,這樣,我們就完成了一個簡單的性能分析,類似地,我們啟動dotMemery,此時,可以得到下面的結果:

這里,我們通過<YourApp>.runtimeconfig.json檔案,設定了GC堆的最大值是1M,而每次向串列中添加超過85K的byte陣列時,當前物件會被分配到大物件堆上,通過這張圖我們可以很清楚的看到,整個曲線中藍色區域的 LOH 占了絕對的比例,換言之,幾乎所有的記憶體都是分配到大物件堆(LOH)上的,此外,有些小物件從0代升到了1代,在這個例子中,由于可分配的記憶體不足,最終引發了OutOfMemoryException,而這和我們看到的結果是相符合的:
{
"runtimeOptions": {
"tfm": "netcoreapp3.1",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "3.1.0"
},
"configProperties": {
"System.GC.HeapHardLimit": 1048576
}
}
}
從Dump檔案進行分析
到此為止,關于 dotTrace 和 dotMemery 的使用就基本上講解完啦!可能這時候有些朋友會產生疑問,如果性能問題發生在生產環境怎么辦啊,不錯,這里我們除錯的都是本地的程序,生產環境是沒有機會讓你這樣去搞的,此時,我們可以借助記憶體轉儲檔案(Dump)檔案,它是行程的記憶體鏡像,可以把程式的執行狀態通過除錯器保存在Dump檔案中,試想一下,如果程式在前一秒崩潰了,而你在這一瞬間獲得了當時程式的狀態資訊,相當于拿到了“故障”遺留在現場的“罪證”,在Windows系統中創建Dump檔案是非常簡單的,通過任務管理器->創建轉儲檔案即可完成,我們繼續使用上面提到的例子:

其實,拿到Dump檔案以后,分析它的工具非常多,比如常見的WinDBG、DebugDiag等等,這里我們可以直接使用 dotMemery ,因為它本身就支持Dump檔案的匯入,相比前面兩種在使用上要更加友好一點,此時,匯入這個Dump檔案,我們就可以獲得下面的結果:


這和我們前面分析出的結論是一致的,即,幾乎所有的記憶體都是分配到大物件堆(LOH)上的,除此以外,針對.NET Core,官方提供了
dotnet-dump和dotnet-gcdump兩個命令列工具,可以通過下面的命令安裝:
dotnet tool install -g dotnet-dump
dotnet tool install -g dotnet-gcdump
這兩個命令同樣可以對記憶體進行分析,關于更多的.NET Core的診斷教程,請參考:https://docs.microsoft.com/zh-cn/dotnet/core/diagnostics/event-counter-perf,這些細節都是針對.NET Core的,可能不具有普適性,感興趣的朋友可以自行前去了解,和大多數JetBrains的應用一樣,這些程式都有 Visual Studio 的擴展程式,可以直接集成到 Visual Studio 中,這個同樣看個人喜好,不再詳細講解,
本文小結
結合一個簡單的示例程式,本文簡單地介紹了來自 JetBrains 的兩款軟體 dotTrace 和 dotMemery 的基本使用,以及如何通過記憶體轉儲檔案(Dump)對生產環境中的記憶體進行診斷,在以往的關于程式性能優化的經歷中,我個人還使用過 ANTS-Performance-Profiler 這個軟體,但體驗上感徑訓是 dotTrace 和 dotMemery 稍微好用一點,而對于更一般的代碼角度的性能分析,我推薦一個輕量級的專案MiniProfiler,性能優化不能靠猜,可是從初中就開始學的“控制變數法”未嘗不是一個不錯的思路,刷 LeetCode 的這段時間,一個最大的感悟就是,程式的性能,真的是一點一點的優化出來的,就拿最簡單的排序來說,你真的要在上面提交很多次,才能漸漸地明白為什么說有些排序演算法是“不穩定”的,也許,現在硬體水平越來越好,我們不必像前輩們一樣“錙銖必較”,可這一切其實很都公平,你寫代碼的時候有多浪費,你玩游戲的時候就有多心疼,這里要特別表揚育碧對叛變這一作的優化,好了,這就是這篇博客的內容啦,謝謝大家,晚安!
參考鏈接
- https://www.jetbrains.com/zh-cn/profiler/
- https://www.jetbrains.com/zh-cn/dotmemory
- https://docs.microsoft.com/zh-cn/dotnet/core/diagnostics/debug-linux-dumps
- https://docs.microsoft.com/zh-cn/dotnet/core/diagnostics/debug-memory-leak
- https://docs.microsoft.com/zh-cn/dotnet/core/diagnostics/debug-highcpu?tabs=windows
CSDN認證博客專家
.NET
Python
偽·全堆疊攻城獅
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/202563.html
標籤:其他
