首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

CLR 核心深度解析:記憶體問題診斷實戰指南

     編輯:關於.NET

記憶體問題常見徵兆

  • 拋出 OutOfMemoryException (OOM)。
  • 進程記憶體異常增長、原因不明。
  • GC 清理緩慢、Managed Heap 碎片過多。
  • CPU 過度佔用(常因頻繁 GC)。

核心工具鏈

  • 效能計數器:.NET CLR Memory 類別 (% Time in GC, Allocated Bytes/sec, Gen 0/1/2 Collections, LOH Size, Promoted Memory 等)。
  • WinDbg + SOS.dll:.loadby sos mscorwks,指令 !dumpheap -stat, !gcroot, !finalizequeue, !eeheap -gc, !threads 等。
  • 工作管理員:觀察 VM Size (Private Bytes) 趨勢判斷洩漏。
  • 完整記憶體傾印:.dump /ma file.dmp,離線分析生產環境問題。

關鍵效能計數器解讀

計數器關注重點
% Time in GC> 50% 需調查;< 10% 通常無需優化。
Allocated Bytes/sec配置速率高 → 頻繁 GC。注意計數器只在 GC 時更新。
Gen 0/1/2 Collections理想比例:10 次 Gen1 : 1 次 Gen2。Gen2 過多代表物件過早晉升。
Promoted Memory (Gen 0/1)Gen1 晉升率過高 → 「中年危機」:物件存活夠久晉升 Gen2,但又不夠久留在 Gen2,增加 Gen2 GC 成本。
# Total committed/reserved BytesGC Heap 實際佔用與保留虛擬記憶體。
# Induced GC手動 GC.Collect() 次數過多視為 Bug。

確認 OOM 來源:Managed vs Native

  1. WinDbg 執行 !pe (PrintException) 檢視最後 Managed 例外。
  2. 若無,用 ~*kb 檢視所有執行緒堆疊,搜尋 mscorwks!RaiseTheException。
  3. 或用 !threads 最後一欄查看各執行緒最近 Managed 例外。
  4. 找不到 Managed OOM → 為 Native 代碼問題。

OOM 兩大原因與排查

1. 虛擬記憶體耗盡 (32-bit 程序)

  • !address -summary 查看 Largest free region。若 < 64MB (32-bit) 或 < 1GB (64-bit) → 虛擬記憶體碎片化。
  • !eeheap -gc 對應 GC 段落位置,判斷碎片是否由 Managed Heap 造成。
  • 常見碎片來源:大量載入/卸載小組件、COM Interop 載入大量 COM DLL、ASP.NET debug=true 編譯產生大量組件。

2. 實體記憶體不足

  • 工作管理員「效能」頁籤觀察 Available Bytes 接近零。
  • GC 分段配置實體頁,系統實體記憶體耗盡時拋 OOM。

分析 Managed Heap 物件佔用

  1. 在 Gen2 GC 結束時中斷:bp mscorwks!WKS::GCHeap::RestartEE "j (dwo(mscorwks!WKS::GCHeap::GcCondemnedGeneration)==2) 'kb';'g'"
  2. !dumpheap -stat 依類型統計大小,從尾端最大物件著手分析。
  3. 字串佔用大:用 !dumpheap -type System.String -min 150 -max 200 檢視內容,常見重複前綴 (如 PendingOrder-) → 適用字串驻留 (String.Intern)。
  4. 其他類型:用 !gcroot <addr> 追蹤根參考,找出為何存活。

物件未被回收的三大原因

  1. 強參考仍存在 (用 !gcroot 確認)。
  2. 上次該代 GC 時物件尚存活。
  3. 物件已死但該代 GC 尚未觸發 (實體記憶體壓力大時 GC 更積極)。
  4. 特殊:Finalizer 執行緒卡住 (STA 不處理訊息) → 物件滯留 Finalization Queue。用 !finalizequeue 檢視 Ready for finalization 數量;!threads -special 觀察 Finalizer 執行緒堆疊。臨時解法:GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();,長期改用 Thread.Join。

Heap 碎片分析

  • !dumpheap -type Free -stat 統計 Free 物件總大小 = 碎片量。
  • Gen0 碎片無害 (可分配);Gen1/Gen2 碎片需壓縮填補。
  • 主要成因:過度 Pinned (釘住)。用 !gchandles 統計 Pinned Handles,!objsize 找出釘住物件。
  • LOH 不壓縮,相鄰 Free 會自然合併,碎片影響較小。

GC 耗時測量

  • % Time in GC 計數器:最近一次 GC 耗時 / 上次 GC 結束後經過時間。
  • 範例:采樣間隔 1 秒,% Time in GC = 3% → GC 耗時 ~30ms。
  • 高 CPU 配合高 % Time in GC → 配置速率過高或物件存活率高導致 GC 頻繁/耗時。

高 CPU 使用率排查

  • GC 期間 CPU 必然飆高。用 CPU Profiler 找出非 GC 熱點函數。
  • 若 GC 佔用過多 CPU:降低配置速率、減少長壽命物件、避免大物件頻繁配置釋放。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved