某天收到運維線上警報,伺服器記憶體告警,需要處理一下。此時通過瀏覽器開啟頁面,系統可以正常存取,但是有明顯示卡頓。為了不影響客戶使用,先重啟了服務釋放了記憶體。由於該專案平時存取量並不大,因此隨著程式執行記憶體佔用率的增長比較緩慢,直到第三天才發現從原本的10%跳到了45%。初步懷疑有記憶體漏失問題需要進行線上排查。
偵錯記憶體漏失教學 - .NET | Microsoft Learn
在伺服器上安裝dotnet tool下的診斷工具
dotnet-counters
是一個效能監視工具,用於臨時執行狀況監視和初級效能調查。dotnet-dump
是在未涉及任何本機偵錯程式的情況下收集和分析 Windows、Linux 和 macOS 轉儲的方法。 可以執行 SOS 命令來分析崩潰和垃圾回收器 (GC)dotnet tool install -g dotnet-counters
dotnet tool install -g dotnet-dump
安裝過程中可能遇到的問題:
1. Https 存取證書問題
NuGet.Protocol.Core.Types.FatalProtocolException: 無法載入源 https://api.nuget.org/v3/index.json 的服務索引。
---> System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid because of errors in the certificate chain: UntrustedRoot
執行openssl version -a
查詢安裝目錄,然後執行cp /etc/pki/tls/cert.pem /usr/local/openssl-1.1.1o/ssl/
命令將預設證書放到SSL目錄
2. dotnet tool 版本問題
Unable to find package dotnet-counters. No packages exist with this id in source(s): nuget.org
或
error NU1202: Package dotnet-counters 8.0.452401 is not compatible with net5.0 (.NETCoreApp,Version=v5.0) / any. Package dotnet-counters 8.0.452401 supports: net6.0 (.NETCoreApp,Version=v6.0)
根據伺服器上的.NET SDK版本,指定版本號完成的安裝。版本號:https://www.nuget.org/packages/dotnet-counters
通過Top
命令檢視程序資源佔用情況,輸入M
按記憶體使用百分比排序,並獲取到程式PID。
執行dotnet-counters monitor -p 6309
,檢視程式執行資訊,發現Gen 2和LOH佔用大量記憶體。
關於GC相關內容可以看官方檔案ASP.NET Core 中的記憶體管理和模式
(上面兩張截圖是事後擷取的,作演示用)
執行dotnet-dump collect -p 6309
,在當前目錄生成程式記憶體dump檔案
執行dotnet-dump analyze core_20231026_162034
分析dump檔案,進入互動式shell,通過SOS命令分析檔案內容。相關SOS命令可以檢視官方檔案dotnet-dump 診斷工具
執行dumpheap -stat -min 85000
,檢視大於85000 B的物件,可以看到有78個大System.Byte[]
型別的物件。
執行dumpheap -mt -min 85000
檢視具體的大物件列表。GC 會為大型物件(大於 85,000 位元組)建立特殊記憶體區域,稱為大型物件堆 (LOH)。
執行dumpobj 00007fdfbd54ead0
,檢視物件內容。
比對下各個物件內容,大部分為亂碼,少數顯示Exif
和JFIF
等字元。聯想到最近專案更新中包含了一部分圖片處理相關功能,因此初步懷疑是ImageResize
的程式碼有問題,導致了記憶體漏失。
執行gcroot 00007f8c51457968
,檢視物件參照情況,沒有任何物件參照。
重寫修改了程式碼後上線,隨著相關介面被呼叫,程式的記憶體佔用依舊會上漲。通過dotnet-counters
檢視發現Gen2和LOH佔用大量記憶體沒有被回收。
查閱官方檔案的過程中發現,臨時大物件會導致第 2 代 GC,但是不理解為什麼這些記憶體沒有被回收。
2023-10-27 更新:原本專案中使用System.Drawing
對圖片進行壓縮處理,綜合考慮後選擇使用SixLabors.ImageSharp
重寫這部分程式碼。目前更新線上上觀察一段時間。