在 .NET Core 中偵測記憶體流失

本文適用于: ✔️ .net CORE 3.1 SDK 和更新版本

當您的應用程式參考不再需要執行所需工作的物件時,可能會發生記憶體流失。 參考提到的物件會讓垃圾收集行程無法回收所使用的記憶體,通常會導致效能降低,最後 OutOfMemoryException 可能會擲回。

本教學課程示範使用 .NET 診斷 CLI 工具來分析 .NET Core 應用程式中記憶體流失的工具。 如果您在 Windows 上,您可以使用 Visual Studio 的記憶體診斷工具來偵測記憶體流失。

本教學課程使用範例應用程式,其設計目的是刻意洩漏記憶體。 範例是以練習的形式提供。 您也可以分析意外流失記憶體的應用程式。

在本教學課程中,您將:

  • 檢查 dotnet 計數器的 managed 記憶體使用量。
  • 產生傾印檔案。
  • 使用傾印檔案分析記憶體使用量。

必要條件

教學課程會使用:

本教學課程假設範例和工具已安裝且可供使用。

檢查受控記憶體使用量

在您開始收集診斷資料以協助我們根本原因的情況下,您必須確定您實際上看到記憶體流失 (記憶體成長) 。 您可以使用 dotnet 計數器 工具來確認。

開啟主控台視窗,並流覽至您下載並解壓縮 範例 debug 目標的目錄。 執行目標:

dotnet run

從個別的主控台中,尋找處理序識別碼:

dotnet-counters ps

輸出應該會類似:

4807 DiagnosticScena /home/user/git/samples/core/diagnostics/DiagnosticScenarios/bin/Debug/netcoreapp3.0/DiagnosticScenarios

現在,請使用 dotnet 計數器 工具檢查 managed 記憶體使用量。 --refresh-interval指定重新整理之間的秒數:

dotnet-counters monitor --refresh-interval 1 -p 4807

即時輸出應如下所示:

Press p to pause, r to resume, q to quit.
    Status: Running

[System.Runtime]
    # of Assemblies Loaded                           118
    % Time in GC (since last GC)                       0
    Allocation Rate (Bytes / sec)                 37,896
    CPU Usage (%)                                      0
    Exceptions / sec                                   0
    GC Heap Size (MB)                                  4
    Gen 0 GC / sec                                     0
    Gen 0 Size (B)                                     0
    Gen 1 GC / sec                                     0
    Gen 1 Size (B)                                     0
    Gen 2 GC / sec                                     0
    Gen 2 Size (B)                                     0
    LOH Size (B)                                       0
    Monitor Lock Contention Count / sec                0
    Number of Active Timers                            1
    ThreadPool Completed Work Items / sec             10
    ThreadPool Queue Length                            0
    ThreadPool Threads Count                           1
    Working Set (MB)                                  83

將焦點放在這一行:

    GC Heap Size (MB)                                  4

您可以在啟動後看到 managed 堆積記憶體是 4 MB。

現在,按下 URL https://localhost:5001/api/diagscenario/memleak/20000

觀察記憶體使用量已成長至 30 MB。

    GC Heap Size (MB)                                 30

藉由監看記憶體使用量,您可以放心地指出記憶體正在成長或流失。 下一步是收集正確的記憶體分析資料。

產生記憶體傾印

在分析可能的記憶體流失時,您需要存取應用程式的記憶體堆積。 然後您就可以分析記憶體內容。 查看物件之間的關聯性,您可以建立理論,以瞭解為何無法釋放記憶體。 常見的診斷資料來源是 Windows 上的記憶體傾印或 Linux 上對等的核心轉儲。 若要產生 .NET Core 應用程式的傾印,您可以使用 dotnet 傾印工具。

使用先前啟動的 範例 debug 目標 ,執行下列命令以產生 Linux core 傾印:

dotnet-dump collect -p 4807

結果是位於相同資料夾的核心傾印。

Writing minidump with heap to ./core_20190430_185145
Complete

重新開機失敗的進程

收集傾印之後,您應該有足夠的資訊來診斷失敗的進程。 如果在實際執行伺服器上執行失敗的程式,現在是重新開機處理常式的短期補救的理想時間。

在本教學課程中,您現在已完成 範例 debug 目標 ,而且您可以將它關閉。 流覽至啟動伺服器的終端機,然後按下 Ctrl + C

分析核心傾印

現在您已產生核心傾印,請使用 dotnet 傾印工具來分析傾印:

dotnet-dump analyze core_20190430_185145

其中 core_20190430_185145 是您想要分析的核心轉儲的名稱。

注意

如果您發現抱怨找不到 libdl.so 的錯誤,您可能必須安裝 libc6 開發 套件。 如需詳細資訊,請參閱 Linux 上 .NET Core 的必要條件

您將會看到可輸入 SOS 命令的提示。 通常,您想要查看的第一件事是 managed 堆積的整體狀態:

> dumpheap -stat

Statistics:
              MT    Count    TotalSize Class Name
...
00007f6c1eeefba8      576        59904 System.Reflection.RuntimeMethodInfo
00007f6c1dc021c8     1749        95696 System.SByte[]
00000000008c9db0     3847       116080      Free
00007f6c1e784a18      175       128640 System.Char[]
00007f6c1dbf5510      217       133504 System.Object[]
00007f6c1dc014c0      467       416464 System.Byte[]
00007f6c21625038        6      4063376 testwebapi.Controllers.Customer[]
00007f6c20a67498   200000      4800000 testwebapi.Controllers.Customer
00007f6c1dc00f90   206770     19494060 System.String
Total 428516 objects

在這裡,您可以看到大部分的物件都是 StringCustomer 物件。

您可以使用 dumpheap 命令,搭配方法資料表 (MT) 來取得所有 String 實例的清單:

> dumpheap -mt 00007faddaa50f90

         Address               MT     Size
...
00007f6ad09421f8 00007faddaa50f90       94
...
00007f6ad0965b20 00007f6c1dc00f90       80
00007f6ad0965c10 00007f6c1dc00f90       80
00007f6ad0965d00 00007f6c1dc00f90       80
00007f6ad0965df0 00007f6c1dc00f90       80
00007f6ad0965ee0 00007f6c1dc00f90       80

Statistics:
              MT    Count    TotalSize Class Name
00007f6c1dc00f90   206770     19494060 System.String
Total 206770 objects

您現在可以在實例上 System.String 使用 gcroot 命令,以查看物件的根目錄和原因。 請耐心等候,因為此命令需要幾分鐘的時間,且具有 30 MB 的堆積:

> gcroot -all 00007f6ad09421f8

Thread 3f68:
    00007F6795BB58A0 00007F6C1D7D0745 System.Diagnostics.Tracing.CounterGroup.PollForValues() [/_/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/CounterGroup.cs @ 260]
        rbx:  (interior)
            ->  00007F6BDFFFF038 System.Object[]
            ->  00007F69D0033570 testwebapi.Controllers.Processor
            ->  00007F69D0033588 testwebapi.Controllers.CustomerCache
            ->  00007F69D00335A0 System.Collections.Generic.List`1[[testwebapi.Controllers.Customer, DiagnosticScenarios]]
            ->  00007F6C000148A0 testwebapi.Controllers.Customer[]
            ->  00007F6AD0942258 testwebapi.Controllers.Customer
            ->  00007F6AD09421F8 System.String

HandleTable:
    00007F6C98BB15F8 (pinned handle)
    -> 00007F6BDFFFF038 System.Object[]
    -> 00007F69D0033570 testwebapi.Controllers.Processor
    -> 00007F69D0033588 testwebapi.Controllers.CustomerCache
    -> 00007F69D00335A0 System.Collections.Generic.List`1[[testwebapi.Controllers.Customer, DiagnosticScenarios]]
    -> 00007F6C000148A0 testwebapi.Controllers.Customer[]
    -> 00007F6AD0942258 testwebapi.Controllers.Customer
    -> 00007F6AD09421F8 System.String

Found 2 roots.

您可以看到 String ,直接由 Customer 物件保留,且由 CustomerCache 物件間接保留。

您可以繼續傾印物件,以查看大部分 String 的物件都遵循類似的模式。 此時,調查已提供足夠的資訊來識別程式碼中的根本原因。

此一般程式可讓您識別主要記憶體流失的來源。

清除資源

在本教學課程中,您已啟動範例 web 伺服器。 此伺服器應該已關閉,如「 重新開機失敗的 程式」一節所述。

您也可以刪除已建立的傾印檔案。

另請參閱

後續步驟