写在前面
生产环境的应用出问题,往往不能直接 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 是事后分析的"黑匣子",平时配好自动抓取,出问题才有料可查;两个平台工具不同但目标一致。