一:背景
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介入之前,它起来了一个很好的拦截筛选作用。