.NET Dump 诊断:从抓取到分析(Linux + Windows)

写在前面

生产环境的应用出问题,往往不能直接 Attach 调试器——服务正在跑,停不了;问题转瞬即逝,复现不了;线上机器没装 IDE。这时候 dump(内存转储) 就是排错的救命稻草。

.NET 应用现在普遍 Linux 部署(容器、K8s),但开发机往往是 Windows,分析工具链也大不相同。本文对比讲解两个平台如何抓 dump、如何分析,以及内存泄漏、CPU 飙高、死锁、崩溃四大典型场景。


一、什么是 dump

1.1 定义

dump 是某个时刻进程内存的完整快照,相当于给运行中的程序"拍一张照片"。

1
2
3
4
5
6
7
8
9
dump 包含什么:
  - 所有线程的调用栈
  - 托管堆上的所有对象(.NET 对象)
  - 非托管内存(C++ 层)
  - GC 状态(各代堆大小)
  - 锁信息(Monitor、SyncBlock)
  - 异常对象
  - AppDomain、Module 信息
  - 寄存器和线程上下文

1.2 为什么用 dump 而不是直接调试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
直接 Attach 调试器的问题:
  ✗ 需要停住进程,影响线上服务
  ✗ 需要源码和符号文件
  ✗ 问题可能已经发生过了,现场不在

dump 的优势:
  ✓ 抓取快(几秒),对线上影响小
  ✓ 离线分析,不占用生产机器
  ✓ 可以反复分析,团队协作
  ✓ 捕获"案发现场",事后也能查

二、Linux 和 Windows 的根本差异

很多人觉得"dump 不就那样",但两个平台从文件格式、抓取工具到分析工具链差异很大。先建立整体认知。

2.1 dump 文件格式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
平台            格式                        抓取工具
─────────────────────────────────────────────────────────
Linux           minidump(.NET 工具抓的)    dotnet-dump / createdump
Linux           ELF core dump(OS 抓的)     gcore / 系统崩溃
Windows         Windows minidump (.dmp)     dotnet-dump / Procdump / 任务管理器

关键点:
  .NET 官方工具(dotnet-dump、createdump)在两个平台都生成
  统一的 minidump 格式 → 抓出来的 dump 可以跨平台分析
  (Linux 上抓的 dump,拷到 Windows 用 WinDbg 也能分析,反之亦然)

  但 OS 原生工具抓的不一样:
    gcore(Linux)→ ELF core dump,需要 LLDB 分析
    任务管理器(Windows)→ minidump,WinDbg / VS / dotnet-dump 都能开

2.2 抓取方式的差异

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Linux 的特点:
  ✓ 命令行为主(服务器无 GUI)
  ✓ 工具:dotnet-dump、createdump、gcore
  ✓ 环境变量配置 OOM 自动抓(生产标配)
  ✓ 容器环境要考虑镜像精简、权限
  → 偏"运维向",脚本化

Windows 的特点:
  ✓ GUI + 命令行都有
  ✓ 工具:任务管理器(一键)、Procdump(条件触发)、WinDbg
  ✓ Procdump 的条件触发(CPU/内存/异常阈值)极其强大
  ✓ 符号服务器集成完善
  → 偏"开发向",图形化体验好

2.3 分析方式的差异

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Linux 的分析:
  ✓ 命令行为主
  ✓ dotnet-dump analyze(官方,推荐)
  ✓ LLDB + SOS 插件(老牌,功能全但配置麻烦)
  ✗ 几乎没有图形化工具

Windows 的分析:
  ✓ 图形化为主
  ✓ WinDbg(最强,命令 + GUI)
  ✓ Visual Studio(有源码时最舒服)
  ✓ PerfView(微软,免费,内存分析强)
  ✓ dotMemory(JetBrains,收费,体验最好)
  → 工具链远比 Linux 丰富

2.4 平台对比总览

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
维度              Linux                     Windows
────────────────────────────────────────────────────────────
抓取主力          dotnet-dump / createdump   Procdump / 任务管理器
自动抓 OOM        环境变量(DOTNET_Dbg*)    Procdump -e 守护
GUI 抓取          无                         任务管理器 / Process Explorer
分析主力          dotnet-dump analyze        WinDbg / VS
图形化分析        无                         WinDbg / VS / PerfView / dotMemory
符号              .pdb + dotnet 符号         符号服务器 _NT_SYMBOL_PATH
容器调试          主战场(Docker/K8s)       Windows 容器较少
典型场景          生产排错                   开发机分析 + 生产抓取

实战常见组合:生产环境是 Linux,抓完 dump 拷到 Windows 开发机,用 WinDbg/VS 图形化分析。这是最顺手的姿势。


三、跨平台工具:dotnet-dump 和 dotnet-gcdump

这两个是 .NET 官方 CLI 工具,Linux 和 Windows 完全一样,是两个平台共通的基础。

3.1 dotnet-dump

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 安装(两个平台相同)
dotnet tool install -g dotnet-dump

# 找进程
dotnet-dump ps

# 抓 Full dump
dotnet-dump collect -p 12345
dotnet-dump collect -p 12345 --type Full       # 完整(默认)
dotnet-dump collect -p 12345 --type Heap       # 堆
dotnet-dump collect -p 12345 --type Triage     # 脱敏,适合分享

# 指定输出
dotnet-dump collect -p 12345 -o ./myapp.dmp

# 直接分析(跨平台)
dotnet-dump analyze ./myapp.dmp

3.2 dotnet-gcdump(轻量堆 dump)

1
2
3
4
5
dotnet tool install -g dotnet-gcdump

# 只抓托管堆,文件几 MB
dotnet-gcdump collect -p 12345
dotnet-gcdump collect -p 12345 -o ./myapp.gcdump
1
2
3
什么时候用哪个:
  Full dump(dotnet-dump)   — 全面排查,看线程/锁/异常,文件大
  gcdump(dotnet-gcdump)    — 只查内存,文件小,VS/dotMemory 可打开

四、Linux 平台:抓 dump

4.1 createdump(.NET 运行时自带)

1
2
3
4
5
6
7
8
# 路径在 .NET runtime 旁边
# /usr/share/dotnet/shared/Microsoft.NETCore.App/<版本>/createdump

# 抓 Full dump
/usr/share/dotnet/shared/Microsoft.NETCore.App/8.0.0/createdump 12345

# 找路径的快捷方式
ls $(dirname $(readlink -f $(which dotnet)))/shared/Microsoft.NETCore.App/*/createdump
1
2
3
4
特点:
  .NET 自带,无需额外安装
  生产镜像里直接可用
  抓的是 minidump,dotnet-dump / LLDB 都能分析

4.2 gcore(系统通用,抓 ELF core)

1
2
3
4
5
6
# 通用工具,抓任何进程
gcore 12345
# 输出:core.12345(ELF core dump 格式)

# 指定输出名
gcore -o /tmp/myapp 12345
1
2
3
4
注意:
  gcore 抓的是 ELF core dump(不是 minidump)
  分析需要 LLDB + SOS,不能直接用 dotnet-dump analyze
  通常推荐用 createdump 或 dotnet-dump,除非它们不可用

4.3 OOM / 崩溃自动抓(生产标配)

这是 Linux 生产环境最重要的配置——进程崩溃时自动留一份 dump。

1
2
3
4
5
6
7
8
9
# 启动应用前设置环境变量
export DOTNET_DbgEnableMiniDump=1
export DOTNET_DbgMiniDumpType=4           # 4 = Full
export DOTNET_DbgMiniDumpName=/tmp/coredump.%p

# 启动应用
dotnet MyApp.dll

# 进程 OOM 或未处理异常时,自动在 /tmp/ 生成 dump
1
2
3
4
5
6
7
8
DOTNET_DbgMiniDumpType 取值:
  1 — Mini(小)
  2 — Heap(堆)
  3 — Triage(脱敏)
  4 — Full(完整,推荐)

这个配置对 Windows 也有效,但 Linux 生产环境用得最多
(因为 Linux 服务通常在容器里,崩了就重启,没自动 dump 就彻底没现场)

4.4 容器环境(Docker / K8s)

容器里通常镜像精简(alpine 无 glibc),工具缺失,要特殊处理。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 方案一:宿主机抓(容器进程对宿主机可见)
docker top myapp                      # 找容器内进程在宿主机的 PID
dotnet-dump collect -p <宿主机PID>     # 在宿主机抓

# 方案二:进容器抓(镜像里要有工具)
docker exec -it myapp dotnet-dump collect -p 1

# 方案三:用 SDK 镜像临时调试(精简镜像的救星)
kubectl debug pod/myapp-pod -it \
  --image=mcr.microsoft.com/dotnet/sdk:8.0 \
  --target=myapp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# K8s Pod 配置 OOM 自动抓 + 持久化 dump
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: myapp
    image: myapp:latest
    env:
    - name: DOTNET_DbgEnableMiniDump
      value: "1"
    - name: DOTNET_DbgMiniDumpType
      value: "4"
    - name: DOTNET_DbgMiniDumpName
      value: "/dumps/coredump.%p"
    volumeMounts:
    - name: dumps
      mountPath: /dumps
  volumes:
  - name: dumps
    emptyDir: {}

五、Windows 平台:抓 dump

5.1 任务管理器(最简单)

1
2
3
4
5
6
1. Ctrl+Shift+Esc 打开任务管理器
2. 切换到"详细信息"(Details)标签
3. 右键目标进程 → 创建转储文件(Create dump file)
4. 弹窗显示路径,通常在 %TEMP%\<进程名>.DMP

适合:临时抓一下,啥工具都没装

5.2 Procdump(Sysinternals,最强大)

Windows 抓 dump 的王牌,条件触发是它最强大的能力。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 下载:https://learn.microsoft.com/sysinternals/procdump

# 抓一次 Full dump
procdump -ma 12345
procdump -ma 12345 C:\dumps\           # 指定目录

# CPU 超过 80% 持续 5 秒时抓
procdump -ma -c 80 -s 5 12345

# 内存超过 2GB 时抓
procdump -ma -m 2000 12345

# 进程崩溃时自动抓(-e 异常触发,守护模式)
procdump -ma -e 1 -f "" 12345

# 特定异常触发(如 OutOfMemoryException)
procdump -ma -e 1 -f "OutOfMemoryException" 12345

# 抓 5 个,间隔 5 秒(看趋势)
procdump -ma -n 5 -s 5 12345

# 进程无响应时抓
procdump -ma -t 12345
1
2
3
4
Procdump vs Linux 的 OOM 环境变量:
  Procdump 更灵活——可以按 CPU、内存、异常类型多种条件触发
  还能抓多个 dump 看趋势
  这是 Windows 平台的独门优势

5.3 Process Explorer(Sysinternals)

1
2
3
4
图形化进程管理器,也能抓 dump:
  右键进程 → Create Dump → Mini / Full

好处:能直观看到进程树、CPU、内存,挑准了再抓

六、Linux 平台:分析 dump

6.1 dotnet-dump analyze(推荐)

1
2
dotnet-dump analyze ./myapp.dmp
# 进入交互式命令行
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
> help                     # 所有命令
> clrstack                 # 当前线程调用栈
> clrstack -all            # 所有线程
> dumpheap -stat           # 堆对象按类型统计
> dumpheap -type User      # 查 User 类型对象
> gcroot <地址>             # 查引用链(内存泄漏关键)
> syncblk                  # 锁状态(死锁排查)
> pe                       # 当前异常
> eeheap -gc               # GC 堆各代大小
> exit

6.2 LLDB + SOS(功能更全)

LLDB 是 Linux 上的 WinDbg 对应物,配合 SOS 插件能分析 .NET dump。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 安装 LLDB
apt install lldb

# 加载 dump
lldb --core ./core.12345

# 加载 SOS 插件
(lldb) plugin load /usr/share/dotnet/shared/Microsoft.NETCore.App/8.0.0/libsosplugin.so

# 之后 SOS 命令可用(和 dotnet-dump analyze 的命令一致)
(lldb) sos dumpheap -stat
(lldb) sos clrstack
1
2
3
4
5
dotnet-dump analyze vs LLDB+SOS:
  dotnet-dump analyze — 官方推荐,开箱即用,命令够用
  LLDB + SOS          — 功能更全(能看原生栈、寄存器),但配置麻烦
  
  绝大多数场景 dotnet-dump analyze 就够了
1
2
3
4
5
Linux 分析的现实:
  命令行为主,没有图形化
  老手用着顺手,新人上手陡
  
  所以常见做法:Linux 抓 dump → scp 拷到 Windows → WinDbg/VS 图形化分析

七、Windows 平台:分析 dump

Windows 的分析工具链远比 Linux 丰富,这是 Windows 平台的最大优势。

7.1 WinDbg(最强)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
安装:
  Microsoft Store  "WinDbg"(新版 WinDbg Preview
  或下载 Windows SDK

加载 dump
  File  Open dump file
   windbg -z myapp.dmp

加载 SOS.NET 调试扩展):
  .loadby sos coreclr      # .NET Core / 5+
  .loadby sos mscorwks     # .NET Framework

WinDbg 独有优势:
   图形化调用栈、变量、内存
   !analyze -v 自动分析崩溃
   SOS 命令和 dotnet-dump 一致(dumpheap/gcroot/syncblk
   支持 .NET Framework 老项目

7.2 Visual Studio(有源码时最舒服)

1
2
3
4
5
6
7
8
直接用 VS 打开 .dmp:
  File → Open → File → 选 .dmp
  → 点"调试托管内存"

适合:
  有源码和符号
  想图形化查看对象树、调用栈
  团队成员不熟命令行

7.3 PerfView(微软,免费)

1
2
3
4
5
6
7
分析 .gcdump 文件的利器:
  打开 .gcdump → 看对象引用树
  打开 .etl(性能日志)→ 看火焰图

特点:
  界面朴素但功能强大
  专门优化内存和性能分析

7.4 dotMemory(JetBrains,收费)

1
2
3
4
5
体验最好的内存分析工具:
  支持 .gcdump / .dmp
  两个 dump 对比,一眼看出增长的对象
  可视化引用链
  自动检测常见泄漏模式

八、跨平台通用分析命令

无论 Linux(dotnet-dump analyze)还是 Windows(WinDbg + SOS),核心分析命令都是同一套(SOS 命令)。这部分两个平台完全通用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
线程相关:
  clrstack                 # 当前线程调用栈
  clrstack -all            # 所有线程(CPU/死锁排查)
  threads                  # 列出所有线程
  setthread <id>           # 切换线程
  dso (dumpstackobjects)   # 栈上的所有对象

堆相关:
  dumpheap -stat           # 按类型统计(内存排查第一步)
  dumpheap -type <类名>     # 查指定类型对象
  dumpheap -mt <MT地址>     # 按方法表查
  dumpheap -min 10000      # 大于 10KB 的对象
  eeheap -gc               # GC 堆各代大小

对象相关:
  do <地址> (dumpobject)    # 看对象内容
  gcroot <地址>             # 引用链(谁引用了它)
  dumpvc <MT> <地址>        # 看值类型

锁相关:
  syncblk                  # 锁状态(死锁)
  dumpheap -thinlock        # 轻量级锁

异常相关:
  pe (print exception)     # 当前异常
  dumpheap -type Exception # 所有异常

异步相关:
  dumpasync                # 异步状态机

九、场景实战(命令跨平台,标注平台习惯)

9.1 内存泄漏

抓取:抓两个 dump 对比(任何平台)

1
2
3
4
# 跨平台
dotnet-gcdump collect -p 12345 -o /tmp/d1.gcdump
# 等 10-30 分钟
dotnet-gcdump collect -p 12345 -o /tmp/d2.gcdump

分析

1
2
3
4
# Linux / Windows 都一样
> dumpheap -stat              # 第一步:找可疑类型
> dumpheap -type UserCache    # 第二步:看具体对象
> gcroot 000002a3b4c50000     # 第三步:找引用链
1
2
3
4
5
6
7
gcroot 输出示例:
  HandleTable (pinned):
    → MyApp.Services.CacheService
    → Dictionary<string, UserCache>
    → UserCache (泄漏对象)

结论:静态 CacheService 的 Dictionary 持有对象,永不释放

9.2 CPU 飙高

dump 看 CPU 不够实时,先看平台对应的实时工具

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Linux / Windows 都能用的官方工具
dotnet-counters monitor -p 12345 System.Runtime[cpu-usage,gen-0-gc-count]
dotnet-stack report -p 12345          # 实时线程栈
dotnet-trace collect -p 12345 --format Speedscope   # 录火焰图

# 也可以抓两个 dump 对比调用栈
dotnet-dump collect -p 12345 -o cpu1.dmp
sleep 5
dotnet-dump collect -p 12345 -o cpu2.dmp

# 分析:找两次调用栈相同的线程(一直在跑同一段代码)
> clrstack -all

9.3 死锁

抓一个 Full dump,用 syncblk

1
2
3
4
5
6
7
8
> syncblk
# 输出会显示:
#   Thread A 持有锁 12,等待锁 15
#   Thread B 持有锁 15,等待锁 12
# → 互相等待 = 死锁

> setthread <Thread A>
> clrstack    # 看 A 卡在哪段代码

9.4 崩溃 / 异常

关键:提前配置自动抓

1
2
3
4
5
6
7
# Linux:环境变量(生产标配)
export DOTNET_DbgEnableMiniDump=1
export DOTNET_DbgMiniDumpType=4
export DOTNET_DbgMiniDumpName=/tmp/crash.%p.dmp

# Windows:Procdump 守护
procdump -ma -e 1 -f "" 12345 C:\dumps\

分析崩溃 dump

1
2
3
4
> pe                    # 看导致崩溃的异常
# 输出:OutOfMemoryException / StackOverflowException / NullReferenceException
> do <异常对象地址>      # 看异常详情
# StackTrace 直接指到崩溃代码位置

十、平台选择建议

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
什么时候在 Linux 上分析:
  ✓ 生产是 Linux,想就地快速看一眼
  ✓ 熟悉命令行,dotnet-dump analyze 够用
  ✓ 没法把 dump 拷出来(体积大、网络受限)

什么时候拷到 Windows 分析:
  ✓ 开发机是 Windows,工具链熟
  ✓ 需要图形化(WinDbg / VS / dotMemory)
  ✓ 团队协作,要发给别人看
  ✓ .NET Framework 老项目(只能 Windows)

最佳实践组合:
  Linux 生产 → 抓 dump(dotnet-dump / createdump / OOM 自动)
       ↓ scp / kubectl cp
  Windows 开发机 → 图形化分析(WinDbg / VS / dotMemory)

十一、生产环境最佳实践

11.1 提前准备(部署时就配好)

1
2
3
4
5
6
7
8
9
# Linux 镜像预装诊断工具 + 开启自动 dump
FROM mcr.microsoft.com/dotnet/aspnet:8.0
RUN dotnet tool install -g dotnet-dump \
 && dotnet tool install -g dotnet-gcdump \
 && dotnet tool install -g dotnet-counters
ENV PATH="$PATH:/root/.dotnet/tools"
ENV DOTNET_DbgEnableMiniDump=1
ENV DOTNET_DbgMiniDumpType=4
ENV DOTNET_DbgMiniDumpName=/dumps/coredump.%p

11.2 符号文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
无论哪个平台,分析 dump 都需要符号(.pdb)才能看到方法和行号:

Linux
   .pdb 放到 DLL 同目录
  或打包归档,分析时对应上

Windows
  配置符号服务器(_NT_SYMBOL_PATH
  WinDbg / VS 会自动从微软下载系统库符号
  set _NT_SYMBOL_PATH=srv*c:\symbols*https://msdl.microsoft.com/download/symbols

11.3 安全注意

1
2
3
4
5
dump 含敏感数据(密码、token、用户信息):
  ✓ 加密存储、访问控制
  ✓ 分析完及时删除
  ✗ 不要提交 Git,不要传公共平台
  ✓ 给第三方用 Triage 类型(dotnet-dump --type Triage 已脱敏)

11.4 排查流程速查

1
2
3
4
5
6
问题          Linux 抓取                Windows 抓取            分析命令
──────────────────────────────────────────────────────────────────────────
内存泄漏      dotnet-gcdump ×2          dotnet-gcdump ×2        dumpheap -stat / gcroot
CPU 飙高     dotnet-dump ×2            procdump -c 80          clrstack -all / dotnet-trace
死锁          dotnet-dump ×1            procdump -ma            syncblk / clrstack
崩溃          OOM 环境变量自动抓        procdump -e 守护         pe / StackTrace

十二、小结

本文从 Linux 和 Windows 两个平台对比讲解了 .NET dump 诊断:

  • 根本差异:文件格式、抓取工具、分析工具链各不相同;但 .NET 官方工具(dotnet-dump)抓的 minidump 跨平台可分析
  • Linux 抓取:dotnet-dump / createdump / gcore + OOM 环境变量自动抓 + 容器调试
  • Windows 抓取:任务管理器一键 / Procdump 条件触发(独门优势)
  • Linux 分析:dotnet-dump analyze / LLDB+SOS(命令行为主)
  • Windows 分析:WinDbg / VS / PerfView / dotMemory(图形化,工具链丰富)
  • 通用命令:SOS 命令(dumpheap/gcroot/syncblk/pe)两个平台完全一致
  • 四大场景:内存泄漏、CPU 飙高、死锁、崩溃的抓取和分析
  • 最佳组合:Linux 生产抓 dump → 拷到 Windows 图形化分析

核心思路:dump 是事后分析的"黑匣子",平时配好自动抓取,出问题才有料可查;两个平台工具不同但目标一致。