DotMemory系列:5. 如何实现自动化抓取和应用自托管
<h2 id="一背景">一:背景</h2><h3 id="1-讲故事">1. 讲故事</h3>
<p>前面几篇我们都是手工安装 dotmemory 软件,然后在程序的合适时机抓取snapshot,这种方式在绝大多数场景下都没有问题,但在一些精细化的场景下,如果能够实现自动化抓取,那就比较🐂👃了,这篇我们就来聊一聊这玩意。</p>
<h2 id="二如何实现自动化抓取">二:如何实现自动化抓取</h2>
<h3 id="1-测试代码">1. 测试代码</h3>
<p>所谓的自动化抓取,意思就是用代码来控制 snapshot 的抓取时机,而不是你在 UI 上点来点去,为了方便测试,先上一段测试代码,参考如下:</p>
<pre><code class="language-C#">
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("快照已生成");
}
}
</code></pre>
<p>上面的代码非常简单,我想在 <code>ProcessData()</code> 方法内的某一个时点通过<code> MemoryProfiler.GetSnapshot</code> 方法自动化抓取snapshot,这个让你在UI上点击,你根本无法做到。</p>
<h3 id="2-dotmemory-集成交互">2. dotmemory 集成交互</h3>
<p>代码里埋好点之后,接下来打开 dotmemory,使用 <code>Using API</code> 模式,这样就相当于给程序开了一个口子,截图如下:</p>
<p><img src="https://img2024.cnblogs.com/blog/214741/202511/214741-20251118110511851-91471306.png" alt="" loading="lazy"></p>
<p>接下来点击 <code>Start</code> 按钮,可以看到程序自动的帮我们生成了一个叫 <code>ProcessDataSnapshot</code> 的snapshot,是不是挺有意思的,截图如下:</p>
<p><img src="https://img2024.cnblogs.com/blog/214741/202511/214741-20251118110511887-1167319627.png" alt="" loading="lazy"></p>
<h2 id="三如何实现自托管">三:如何实现自托管</h2>
<h3 id="1-测试代码-1">1. 测试代码</h3>
<p>所谓的自托管就是让代码自己去下载 <code>Console of DotMemory</code>,全程不需要人为干预,最终会产生一个后缀为 <code>*.dmw</code> 的跟踪文件,参考代码如下:</p>
<pre><code class="language-C#">
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);
}
for (int i = 0; i < 100; i++)
{
_buffers.Add(new byte);
}
for (int i = 0; i < 10000; i++)
{
_charArrays.Add(new char);
}
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("清理完成");
}
}
</code></pre>
<p>上面的代码会在程序运行的三个阶段抓取snapshot,将程序运行起来之后,从下图可以清晰的看到已生成三个 snapshot 快照,是不是挺有意思,截图如下:</p>
<p><img src="https://img2024.cnblogs.com/blog/214741/202511/214741-20251118110511871-83667221.png" alt="" loading="lazy"></p>
<h3 id="2-console-版-dotmemory-分析">2. Console 版 DotMemory 分析</h3>
<p>自托管借助的是 <code>Console 版 DotMemory</code>,不要小看这个 Console,它可以跨平台,也可以集成到各种 自动化发布工具 里面去,这里我就简单演示下在 ubuntu 上如何用 console 版抓 .net 程序的 snapshot 到 windows 上分析。</p>
<p>首先到 https://www.jetbrains.com/dotmemory/download/?section=commandline 上下载安装包,截图如下:</p>
<p><img src="https://img2024.cnblogs.com/blog/214741/202511/214741-20251118110511859-1945546220.png" alt="" loading="lazy"></p>
<pre><code class="language-shell">
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 29620 12:13 pts/1 00:00:00 dotnet Example_6_6.dll
root 3018 19380 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):
dotnet
Attaching to dotnet runtime...
Profiler connected. PID:3007, Core CLR runtime v8.0.15.0
ATTACHED. Getting snapshot...
Saving snapshot... ~5.6 K objects
SNAPSHOT #1 SAVED.
Processing snapshot #1...
SNAPSHOT #1 READY.
Profiler disconnected. PID:3007
Saving workspace...
WORKSPACE SAVED
file:///data/-dotnet.2025-11-17T12-14-10.141.dmw
</code></pre>
<p>从上面的输出可以看到 dmw 文件已生成,接下来将文件导入到 windows 平台上,双击打开。</p>
<p><img src="https://img2024.cnblogs.com/blog/214741/202511/214741-20251118110511897-2015931471.png" alt="" loading="lazy"></p>
<p>哈哈,是不是很完美。。。</p>
<h2 id="四总结">四:总结</h2>
<p>这个系列就先讲到这里吧,常见的功能应该都讲到了,总的来说 dotmemory 这款工具还有很多的缺点和不如意,但在专业的windbg介入之前,它起来了一个很好的拦截筛选作用。</p>
<img src="https://images.cnblogs.com/cnblogs_com/huangxincheng/345039/o_210929020104最新消息优惠促销公众号关注二维码.jpg" width="700" height="300" alt="图片名称" align="center"><br><br>
来源:https://www.cnblogs.com/huangxincheng/p/19236466
頁:
[1]