博客 / 詳情

返回

DotMemory系列:5. 如何實現自動化抓取和應用自託管

一:背景

1. 講故事

前面幾篇我們都是手工安裝 dotmemory 軟件,然後在程序的合適時機抓取snapshot,這種方式在絕大多數場景下都沒有問題,但在一些精細化的場景下,如果能夠實現自動化抓取,那就比較🐂👃了,這篇我們就來聊一聊這玩意。

二:如何實現自動化抓取

1. 測試代碼

所謂的自動化抓取,意思就是用代碼來控制 snapshot 的抓取時機,而不是你在 UI 上點來點去,為了方便測試,先上一段測試代碼,參考如下:


    internal class Program
    {
        static void Main()
        {
            var analyzer = new MemoryAnalyzer();
            analyzer.ProcessData();

            Console.ReadLine();
        }
    }

    public class MemoryAnalyzer
    {
        private readonly List<string> _data = new();

        public void ProcessData()
        {
            // 模擬內存密集型操作
            for (int i = 0; i < 10000; i++)
            {
                _data.Add(new string('x', 1000));
            }

            Console.WriteLine("數據處理完成,3秒後生成快照...");
            Thread.Sleep(3000);

            MemoryProfiler.GetSnapshot("ProcessDataSnapshot");
            Console.WriteLine("快照已生成");
        }
    }

上面的代碼非常簡單,我想在 ProcessData() 方法內的某一個時點通過 MemoryProfiler.GetSnapshot 方法自動化抓取snapshot,這個讓你在UI上點擊,你根本無法做到。

2. dotmemory 集成交互

代碼裏埋好點之後,接下來打開 dotmemory,使用 Using API 模式,這樣就相當於給程序開了一個口子,截圖如下:

接下來點擊 Start 按鈕,可以看到程序自動的幫我們生成了一個叫 ProcessDataSnapshot 的snapshot,是不是挺有意思的,截圖如下:

三:如何實現自託管

1. 測試代碼

所謂的自託管就是讓代碼自己去下載 Console of DotMemory,全程不需要人為干預,最終會產生一個後綴為 *.dmw 的跟蹤文件,參考代碼如下:


    internal class Program
    {
        static void Main(string[] args)
        {
            DotMemory.Init();
            var config = new DotMemory.Config();
            config.SaveToDir(@"E:\testdump");

            DotMemory.Attach(config);

            Console.WriteLine("=== 內存分析開始 ===\n");

            var memoryDemo = new MemoryDemo();

            DotMemory.GetSnapshot("Initial");
            Console.WriteLine("初始快照已生成");

            memoryDemo.CreateObjects();
            DotMemory.GetSnapshot("AfterCreation");
            Console.WriteLine("對象創建後快照已生成");

            memoryDemo.Cleanup();
            DotMemory.GetSnapshot("AfterCleanup");
            Console.WriteLine("清理後快照已生成");

            Console.WriteLine("\n=== 分析完成 ===");

            DotMemory.Detach();
        }
    }

    public class MemoryDemo
    {
        private List<string> _strings = new();
        private List<byte[]> _buffers = new();
        private List<char[]> _charArrays = new();

        public void CreateObjects()
        {
            Console.WriteLine("創建對象中...");

            for (int i = 0; i < 500000; i++)
            {
                _strings.Add($"Data_{i}_{Guid.NewGuid()}_AdditionalPaddingDataToMakeStringLarger");
            }

            for (int i = 0; i < 5000; i++)
            {
                _buffers.Add(new byte[1024 * 200]);
            }

            for (int i = 0; i < 100; i++)
            {
                _buffers.Add(new byte[1024 * 1024 * 5]);
            }

            for (int i = 0; i < 10000; i++)
            {
                _charArrays.Add(new char[1024 * 50]);
            }

            Console.WriteLine($"已創建: {_strings.Count} 個字符串, {_buffers.Count} 個緩衝區, {_charArrays.Count} 個字符數組");
        }

        public void Cleanup()
        {
            Console.WriteLine("清理對象中...");

            _strings.Clear();
            _buffers.Clear();
            _charArrays.Clear();

            GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
            GC.Collect(2, GCCollectionMode.Forced, true, true);
            GC.WaitForPendingFinalizers();

            Console.WriteLine("清理完成");
        }
    }

上面的代碼會在程序運行的三個階段抓取snapshot,將程序運行起來之後,從下圖可以清晰的看到已生成三個 snapshot 快照,是不是挺有意思,截圖如下:

2. Console 版 DotMemory 分析

自託管藉助的是 Console 版 DotMemory,不要小看這個 Console,它可以跨平台,也可以集成到各種 自動化發佈工具 裏面去,這裏我就簡單演示下在 ubuntu 上如何用 console 版抓 .net 程序的 snapshot 到 windows 上分析。

首先到 https://www.jetbrains.com/dotmemory/download/?section=commandline 上下載安裝包,截圖如下:


root@ubuntu2404:/data# tar -xzvf JetBrains.dotMemory.Console.linux-x64.2025.3.0.1.tar.gz

root@ubuntu2404:/data# ps -ef | grep dotnet
root        3007    2962  0 12:13 pts/1    00:00:00 dotnet Example_6_6.dll
root        3018    1938  0 12:13 pts/0    00:00:00 grep --color=auto dotnet
root@ubuntu2404:/data# ./dotMemory.sh get-snapshot 3007 --save-to-dir=./         
dotMemory.sh is deprecated and will soon be removed: Use the dotmemory command instead.
Performs memory profiling of .NET applications

Found 1 process(es):
  [3007] dotnet

Attaching to [3007] dotnet runtime...
Profiler connected. PID:3007, Core CLR runtime v8.0.15.0
  ATTACHED. Getting snapshot...
  [PID:3007] Saving snapshot... ~5.6 K objects
  [PID:3007] SNAPSHOT #1 SAVED.
  [PID:3007] Processing snapshot #1...
  [PID:3007] SNAPSHOT #1 READY.
Profiler disconnected. PID:3007

Saving workspace...
WORKSPACE SAVED
file:///data/[3007]-dotnet.2025-11-17T12-14-10.141.dmw

從上面的輸出可以看到 dmw 文件已生成,接下來將文件導入到 windows 平台上,雙擊打開。

哈哈,是不是很完美。。。

四:總結

這個系列就先講到這裏吧,常見的功能應該都講到了,總的來説 dotmemory 這款工具還有很多的缺點和不如意,但在專業的windbg介入之前,它起來了一個很好的攔截篩選作用。

圖片名稱
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.