使用Windbg快速分析應用記憶體洩露問題

微軟技術棧發表於2021-11-30

Windbg程式除錯是.NET高階開發需要掌握的必備技能,分析記憶體洩露、分析CPU高使用率、分析執行緒阻塞、分析記憶體物件、分析執行緒堆疊、Live Dedugging。這些Debug領域可以說一個技能+場景化應用的結合,如果只依靠Windbg幾個簡單的命令,不理解每個指令在實際Troubleshooting中的作用,是沒有意義的。工欲善其事必先利其器,我們先從常用的命令和示例說起。

微信截圖_20211123172258.png

Windbg 常用命令

先準備一個Dump檔案,建議使用64位應用程式。例如:64位IIS應用的w3wp程式,64位exe程式都可以。如果抓Dump檔案,很簡單:工作管理員-程式-右鍵【建立轉儲檔案】。

然後下載並安裝Windbg,下載連結:https://developer.microsoft.c...,一路下一步,選擇【Debugging Tools for Windows】。

c39515c209c17a72455b7c2b1fafd05c.png
9eb46e13fb7664f66146da0695e46328.png
開啟之後,Ctrl+D,開啟第一步抓的Dump檔案,開始今天的常用命令介紹。

  1. 載入SOS除錯擴充套件dll。
.loadby sos clr
  1. 設定並重新載入除錯符號檔案的命令,將.Net 一些重要的pdb檔案下載到指定的路徑中,載入到Windbg除錯環境中,這樣,我們就可以看到程式在哪一行出錯,執行到哪一行了。
.symfix+ C:\symbols
.reload
  1. 列印當前除錯符號檔案搜尋路徑。
0:000> .sympath
  1. 檢視執行緒池,分析並確認CPU使用率,可以使用哪個指令。
0:000> !threadpool
CPU utilization: 2%
Worker Thread: Total: 19 Running: 2 Idle: 17 MaxLimit: 32767 MinLimit: 4
Work Request in Queue: 0
--------------------------------------
Number of Timers: 2
--------------------------------------
Completion Port Thread:Total: 4 Free: 4 MaxFree: 8 CurrentLimit: 4 MaxLimit: 1000 MinLimit: 4
  1. 檢視執行緒的整體執行情況。

!threads

de34b44a8abb6d486e33d847dd57b26a.png

  1. 查詢指定執行緒的呼叫堆疊,例如34號執行緒。
 ~34s

6231a540b1e24cb9264583b66aff93d5.png

!clrstack

fb017cc908ec5571d2a30b8975e4a05c.png

  1. 檢視執行緒消耗CPU資源情況。

!runaway

527da72a85244c7a7c9ec3157b7907dc.png

第一列是執行緒號,第二列是Total的CPU使用時間

  1. 檢視當前執行緒棧上所有物件的資訊,Dump stack objects。

11042c18267399627d970dcc2192730a.png

  1. 查詢記憶體中指定物件的資訊 Dump object。
!do
  1. 查詢記憶體中指定陣列物件的資訊 Dump Array。

!de

  1. 檢視當前執行緒的堆疊和每行堆疊上的變數資訊。
!clrstack -a
  1. Windbg 附加程式除錯,啟用CLR異常捕獲、檢視異常、檢視異常所線上程堆疊、禁用CLR異常除錯、退出除錯。
sxe clr
g
!pe
!clrstack
sxd clr
qd
  1. 檢視託管堆上記憶體物件的分佈、三個代的資訊。
!eeheap -gc

image.png

  1. 檢視託管堆上載入的Dll。

!eeheap -loader

  1. 查詢記憶體中各類物件的總個數和總記憶體佔用。
!dumpheap  -stat
  1. 查詢記憶體中大物件的個數和物件大小。
!dumpheap -stat  -mt -min 85000
  1. 檢視記憶體的析構佇列的指令。

!finalizequeue

  1. 請輸入檢視物件000000123557DFC0的gcroot的指令。

!gcroot 000000123557DFC0

  1. 檢視執行緒阻塞的指令。

!syncblk

  1. 檢視Dump中所有System.Net.Sockets.Socket物件統計資訊的指令。
!dumpheap -type System.Net.Sockets.Socket -stat

以上是Windbg常用的命令和使用說明,接下來和大家分享使用Windbg分析.NET應用記憶體洩露問題。

使用Windbg分析.NET應用記憶體洩露問題

我們按以下的思路展開今天的分享:

  1. 描述問題背景和現象
  2. 確定問題是否是記憶體洩露
  3. 整理問題分析思路
  4. 動手分析解決
  5. 總結覆盤

先說問題背景

生產環境IIS站點,執行一段時間後,w3wp程式記憶體會漲到2G,同時記憶體不釋放。
image.png

問題確認

開啟效能計數器,我們重點看一段時間內,IIS站點w3wp程式相關的效能計數器的變化:
image.png

效能計數器中:有三個非常重要:

  • .NET CLR Memory/Gen 2 heap size
  • .NET CLR Memory/Gen 1 heap size
  • .NET CLR Memory/Gen 0 heap size

託管堆上的物件有三代:

第 0 代:

這是最年輕的代,其中包含短生存期物件。短生存期物件的一個示例是臨時變數。垃圾回收最常發生在此代中。新分配的物件構成新一代的物件並且為隱式的第 0 代回收,除非它們是大物件,在這種情況下,它們將進入第 2 代回收中的大物件堆。大多數物件通過第 0 代中的垃圾回收進行回收,不會保留到下一代。

第 1 代:

這一代包含短生存期物件並用作短生存期物件和長生存期物件之間的緩衝區。

第 2 代:

這一代包含長生存期物件。長生存期物件的一個示例是伺服器應用程式中的一個包含在程式期間處於活動狀態的靜態資料的物件。
當條件得到滿足時,垃圾回收將在特定代上發生。回收某個代意味著回收此代中的物件及其所有更年輕的代。第 2 代垃圾回收也稱為完整垃圾回收FullGC,因為它回收所有代上的所有物件(即,託管堆中的所有物件)。

倖存和提升:

垃圾回收中未回收的物件也稱為倖存者,並會被提升到下一代。在第 0 代垃圾回收中倖存的物件將被提升到第 1 代;在第 1 代垃圾回收中倖存的物件將被提升到第 2 代;而在第 2 代垃圾回收中倖存的物件將仍為第 2 代。
通過代提升,看物件的存活時間!

Process/Private Bytes
Process/Virtual Bytes
.NET CLR Memory/# Bytes in all Heaps : CLR記憶體託管堆的大小
.NET CLR Memory/Large Object Heap Size: 大物件堆包含其大小為 85,000 個位元組和更多位元組的物件。
image.png

託管堆的記憶體大小增加的趨勢和大物件堆增加的趨勢重疊,可以初步推斷,記憶體的增加和大物件有關係!

整理問題分析思路

連續、間隔抓兩個或者三個Dump,每次抓Dump間隔半個小時,或者一個小時,通過多個Dump對比著看記憶體的增量。
image.png

對比的看每個Dump中:

  • 多核CPU情況下,分析每個GC託管堆的大小 !eeheap –gc
  • 查詢記憶體中各類物件的總個數和總記憶體佔用 !dumpheap –stat
  • 查詢記憶體中大物件的個數和物件大小 !dumpheap –stat -mt -min 85000
  • 如果某一類或者幾類物件的記憶體總佔用很多,分析此類物件 !dumpheap –mt *
  • 多次取樣檢視步驟4中物件的gcroot !gcroot addr

打斷gcroot中任何一個鏈條,釋放物件引用

動手分析解決

多核CPU情況下,分析每個GC託管堆的大小 !eeheap –gc。

image.png

查詢記憶體中各類物件的總個數和總記憶體佔用 !dumpheap –stat。

image.png

查詢記憶體中大物件的個數和物件大小 !dumpheap –stat -mt -min 85000。

image.png

如果某一類或者幾類物件的記憶體總佔用很多,分析此類物件 !dumpheap –mt * -stat。

image.png

大物件字串分析,可以上圖結合具體的程式碼,可以發現是使用者Session會話資料!同時Session會話中包含了整個使用者的許可權資料!

多次取樣檢視步驟4中物件的gcroot !gcroot addr。

image.png

打斷gcroot中任何一個物件的連線引用,完成物件引用的釋放。

總結

分析解決.NET應用程式的記憶體洩露問題,大致的套路是這樣的:

  • 描述問題背景和現象
  • 確定問題是否是記憶體洩露
  • 梳理問題分析思路
  • 動手分析解決
  • 總結覆盤

詳細的分析步驟如下:

  • 多核CPU情況下,分析每個GC託管堆的大小 !eeheap –gc
  • 查詢記憶體中各類物件的總個數和總記憶體佔用 !dumpheap –stat
  • 查詢記憶體中大物件的個數和物件大小 !dumpheap –stat -mt -min 85000
  • 如果某一類或者幾類物件的記憶體總佔用很多,分析此類物件 !dumpheap –mt *
  • 多次取樣檢視步驟4中物件的gcroot !gcroot addr
  • 打斷gcroot中任何一個引用鏈條,釋放物件引用

.NET應用程式的記憶體洩露問題分析,有固定的方法和套路,整個分析過程需要大家深入理解,同時對程式碼的熟悉非常重要,以上分享希望能對大家的日常線上問題分析有所幫助。

微軟最有價值專家(MVP)

image.png

微軟最有價值專家是微軟公司授予第三方技術專業人士的一個全球獎項。28年來,世界各地的技術社群領導者,因其線上上和線下的技術社群中分享專業知識和經驗而獲得此獎項。

MVP是經過嚴格挑選的專家團隊,他們代表著技術最精湛且最具智慧的人,是對社群投入極大的熱情並樂於助人的專家。MVP致力於通過演講、論壇問答、建立網站、撰寫部落格、分享視訊、開源專案、組織會議等方式來幫助他人,並最大程度地幫助微軟技術社群使用者使用Microsoft技術。
更多詳情請登入官方網站:
https://mvp.microsoft.com/zh-cn


歡迎關注微軟中國MSDN訂閱號,獲取更多最新發布!
54f48003ad44029cb57a5d3771a44a47.jpg

相關文章