前言:
近期在專案中出現了幾次服務記憶體資源佔用較高的情況,特回顧梳理下排查過程以及對相應問題的排查方法總結。
一、Dump抓取
抓取dump的方式有多種,下面介紹幾種常用的:
1. 工作管理員中找到程式程式,右鍵選單:建立轉儲存檔案
注意:需要以程式執行的位數執行工作管理員抓取Dump
2. DotNet 全域性工具:dotnet-dump;工具可適用於Windows、Linux、macOS平臺
a) 安裝全域性dotnet-dump工具:
dotnet tool install --global dotnet-dump
b) 使用該工具抓取dump:
dotnet-dump collect [-h|--help] [-p|--process-id] [-n|--name] [--type] [-o|--output] [--diag]
-
- -h|--help:顯示命令列幫助。
- -p|--process-id <PID>:指定從中收集轉儲的程式的 ID 號。
- -n|--name <name>:指定從中收集轉儲的程式的名稱。
- --type <Full|Heap|Mini>:指定轉儲型別,它確定從程式收集的資訊的型別。 有三種型別:Full - 最大的轉儲,包含所有記憶體(包括模組映像)。Heap - 大型且相對全面的轉儲,其中包含模組列表、執行緒列表、所有堆疊、異常資訊、控制程式碼資訊和除對映影像以外的所有記憶體。Mini - 小型轉儲,其中包含模組列表、執行緒列表、異常資訊和所有堆疊。如果未指定,則 Full 為預設型別。
- -o|--output <output_dump_path>:應在其中寫入收集的轉儲的完整路徑和檔名。如果未指定:在 Windows 上預設為 .\dump_YYYYMMDD_HHMMSS.dmp ;在 Linux 上預設為 ./core_YYYYMMDD_HHMMSS ;YYYYMMDD 為年/月/日,HHMMSS 為小時/分鐘/秒。
- --diag:啟用轉儲收集診斷日誌記錄。
3、ProcDump抓取Dump工具:自動抓取
命令語法:
procdump.exe [-mm] [-ma] [-mp] [-mc Mask] [-md Callback_DLL] [-mk] [-n Count] [-s Seconds] [-c|-cl CPU_Usage [-u]] [-m|-ml Commit_Usage] [-p|-pl Counter_Threshold] [-h] [-e [1 [-g] [-b]]] [-l] [-t] [-f Include_Filter, ...] [-fx Exclude_Filter, ...] [-o] [-r [1..5] [-a]] [-at Timeout] [-wer] [-64] { {{[-w] Process_Name | Service_Name | PID} [Dump_File | Dump_Folder]} | {-x Dump_Folder Image_File [Argument, ...]} }
引數說明:
引數 | 說明 |
---|---|
-a | 避免中斷。 需要 -r。 如果觸發器會導致目標因超出併發轉儲限制而長時間掛起,將跳過觸發器。 |
-at | 避免超時中斷。 在 N 秒時取消觸發器的集合。 |
-b | 將除錯斷點視為異常 (否則忽略它們) 。 |
-c | CPU 閾值,用於建立程式的轉儲。 |
-cl | CPU 閾值,低於該閾值可建立程式的轉儲。 |
-d | 呼叫指定 DLL 的名為 MiniDumpCallbackRoutine 的小型轉儲回撥例程。 |
-e | 當程式遇到未經處理異常時編寫轉儲。 包括 1,以在出現第一機會異常時建立轉儲。 |
-f | 篩選第一個可能異常。 支援萬用字元 (*) 。 若要僅顯示名稱而不進行轉儲,請使用空白 ("") 篩選器。 |
-fx | 篩選 (排除) 異常內容和除錯日誌記錄。 支援萬用字元。 |
-g | 在託管程式中作為本機偵錯程式執行, (互操作) 。 |
-h | 如果程式有掛起的視窗, (在至少 5 秒未響應視窗訊息時寫入) 。 |
-i | 將 ProcDump 安裝為 AeDebug 事後偵錯程式。 僅支援使用 -ma、-mp、-d 和 -r 作為附加選項。 |
-k | 克隆到 (-r) 或轉儲收集結束時終止程式 |
-l | 顯示程式的除錯日誌記錄。 |
-m | 記憶體提交閾值(以 MB 為單位)用於建立轉儲。 |
-ma | 編寫包含所有程式記憶體的轉儲檔案。 預設轉儲格式僅包含執行緒和處理資訊。 |
-mc | 編寫自定義轉儲檔案。 包括由指定的十六進位制MINIDUMP_TYPE掩碼 (記憶體) 。 |
-md | 編寫回撥轉儲檔案。 包括由指定 DLL 的名為 MiniDumpCallbackRoutine 的 MiniDumpWriteDump 回撥例程定義的記憶體。 |
-mk | 同時編寫核心轉儲檔案。 包括程式中執行緒的核心堆疊。 使用克隆 (-r) 時,OS 不支援核心轉儲 (-mk) 。 使用多個轉儲大小時,會針對每個轉儲大小執行核心轉儲。 |
-ml | 當記憶體提交低於指定的 MB 值時觸發。 |
-mm | 使用預設模式編寫 (轉儲) 。 |
-mp | 使用執行緒和控制程式碼資訊以及所有讀/寫程式記憶體編寫轉儲檔案。 為了最大程度地減小轉儲大小,將搜尋大於 512MB 的記憶體區域,如果找到,則排除最大的區域。 記憶體區域是大小相同的記憶體分配區域的集合。 刪除此記憶體 (快取) 將Exchange和SQL Server轉儲減少 90% 以上。 |
-n | 退出前要寫入的轉儲數。 |
-o | 覆蓋現有的轉儲檔案。 |
-p | 超過閾值時,對指定效能計數器觸發。 注意:若要在程式有多個例項執行時指定程式計數器,請使用具有以下語法的程式 ID:"\Process (< name > _ < pid >) \counter" |
-pl | 當效能計數器低於指定值時觸發。 |
-r | 使用克隆進行轉儲。 併發限制是可選的 (預設為 1,最大為 5) 。 警告:高併發值可能會影響系統效能。 - Windows 7:使用反射。 OS 不支援 -e。 - Windows 8.0:使用反射。 OS 不支援 -e。 - Windows 8.1+:使用 PSS。 支援所有觸發器型別。 |
-s | 寫入轉儲之前連續秒 (預設值為 10) 。 |
-t | 在程式終止時寫入轉儲。 |
-u | 將 CPU 使用率視為與 -c (一) 。 作為唯一選項,解除安裝 ProcDump 作為事後偵錯程式。 |
-w | 等待指定的程式啟動(如果該程式未執行)。 |
-wer | 將 (最大) 排隊到Windows 錯誤報告。 |
-x | 使用可選引數啟動指定的映像。 如果是應用商店應用程式或包,ProcDump 將在下次啟用時啟動, (啟用) 。 |
-64 | 預設情況下,在 64 位程式上執行時,ProcDump 將捕獲 32 位程式的 32 位Windows。 此選項將替代 以建立 64 位轉儲。 僅用於 WOW64 子系統除錯。 |
-? | 使用 -? -e 檢視示例命令列。 |
示例:
當程式在 5 秒鐘內 CPU 使用率超過 50% 時,最多寫入 2 個名為"w3wp"的程式的微型轉儲:
procdump -ma -s 5 -n 2 -c 50 w3wp
二、Dump分析
根據前面幾方式得到的Dump檔案,下一步就是對Dump進行分析確認問題原因:
1、Windbug Preview 分析:
Windbg Preview 是windows平臺上的一款相當強大的除錯工具,可以從msdn網站下載得到,最新版本包含在windows sdk中,預設會被安裝在C:\Program Files\Debugging Tools for Windows 目錄中,可以直接把這個目錄打包複製到其它機器上使用。
Windbug常用命令:
-
- !analyze -v 自動分析dump
- kv 檢視棧回溯
- .ecxr 顯示當前異常上下文
- .cxr 切換異常幀上下文
- .exr 顯示異常資訊
- .frame 設定當前棧幀
- dv 顯示當前棧幀區域性變數
- dd 顯示記憶體中的資料
- r 檢視暫存器
- lmvm 檢視模組詳細資訊
- r 可以顯示系統崩潰時的暫存器,和最後的命令狀態
- dd 顯示當前記憶體地址,dd 引數:顯示引數處的記憶體
- u 可以顯示反彙編的指令
- kb 顯示call stack 內容
2、VS分析Dump:
a) 使用VS開啟dump檔案:
可以看到該dump的基本資訊,接下來設定符號檔案:
b) 分析Dump中記憶體情況:除錯託管記憶體
可以看到當前託管資源中記憶體佔用情況,如果有多個dump還可以對比dump中記憶體增長變化
檢視物件內容:
c) 檢視當前執行的程式列表:(設定了符號檔案後可以檢視當前堆疊資訊,以及跳轉到程式碼邏輯)
3、dotnet-dump分析:
分析命令:
dotnet-dump analyze <dump_path> [-h|--help] [-c|--command]
SOS命令支援:
命令 | 函式 |
---|---|
soshelp|help |
顯示所有可用命令 |
soshelp|help <command> |
執行指定的命令。 |
exit|quit |
退出互動模式。 |
clrstack <arguments> |
僅提供託管程式碼的堆疊跟蹤。 |
clrthreads <arguments> |
列出正在執行的託管執行緒。 |
dumpasync <arguments> |
顯示有關垃圾回收堆上非同步狀態機的資訊。 |
dumpassembly <arguments> |
顯示有關指定地址處程式集的詳細資訊。 |
dumpclass <arguments> |
顯示有關指定地址處的 EEClass 結構的資訊。 |
dumpdelegate <arguments> |
顯示有關指定地址處的委託的資訊。 |
dumpdomain <arguments> |
顯示所有 AppDomain 和指定域中的所有程式集的資訊。 |
dumpheap <arguments> |
顯示有關垃圾回收堆的資訊和有關物件的收集統計資訊。 |
dumpil <arguments> |
顯示與託管方法關聯的 Microsoft 中間語言 (MSIL)。 |
dumplog <arguments> |
將記憶體中壓力日誌的內容寫入到指定檔案。 |
dumpmd <arguments> |
顯示有關指定地址處的 MethodDesc 結構的資訊。 |
dumpmodule <arguments> |
顯示有關指定地址處的模組的資訊。 |
dumpmt <arguments> |
顯示有關指定地址處的 MethodTable 的資訊。 |
dumpobj <arguments> |
顯示有關位於指定地址處的物件的資訊。 |
dso|dumpstackobjects <arguments> |
顯示在當前堆疊的邊界內找到的所有託管物件。 |
eeheap <arguments> |
顯示有關內部執行時資料結構所使用的程式記憶體的資訊。 |
finalizequeue <arguments> |
顯示所有已進行終結註冊的物件。 |
gcroot <arguments> |
顯示有關對指定地址處的物件的引用(或根)的資訊。 |
gcwhere <arguments> |
顯示傳入引數在 GC 堆中的位置。 |
ip2md <arguments> |
顯示 JIT 程式碼中指定地址處的 MethodDesc 結構。 |
histclear <arguments> |
釋放由 hist* 命令系列使用的任何資源。 |
histinit <arguments> |
從儲存在除錯物件中的壓力日誌初始化 SOS 結構。 |
histobj <arguments> |
顯示與 <arguments> 相關的垃圾回收壓力日誌重定位。 |
histobjfind <arguments> |
顯示在指定地址處引用物件的所有日誌項。 |
histroot <arguments> |
顯示與指定根的提升和重定位相關的資訊。 |
lm|modules |
顯示程式中的本機模組。 |
name2ee <arguments> |
顯示 <argument> 的 MethodTable 和 EEClass 結構。 |
pe|printexception <arguments> |
顯示從 Exception 類派生的 <argument> 的任何物件。 |
setsymbolserver <arguments> |
啟用符號伺服器支援 |
syncblk <arguments> |
顯示 SyncBlock 持有者資訊。 |
threads|setthread <threadid> |
設定或顯示 SOS 命令的當前執行緒 ID。 |
例如:
dotnet-dump analyze "w3wp (2).DMP"
三、案例:
問題現象:xxx服務請求響應耗時較長,導致業務無法正常開展
排查過程:
1、檢視記憶體情況,Oracle連線數量較大
2、檢視執行緒中都為Oracle連線等待:
3、最終確認請求被卡到了Oracle處理
四、總結
1、在排查服務相關問題時,分析dump方式是最直接的方式;而vs方式分析dump是最直接、方便、簡單的方式。
2、通過windbug分析dump是最完整的方式