GC.GetTotalMemory(true) 相减法可粗略估算代码托管内存增量,需先强制全代GC并等待完成,再测基线与执行后差值;它不统计栈、本机资源等,易受LOH、多线程、容器限制干扰。

用 GC.GetTotalMemory 测量某段代码的内存增量
它不是“测代码本身占多少内存”,而是看执行前后托管堆增长了多少——这是最贴近实际需求的粗略估算方式。
常见错误是直接调用 GC.GetTotalMemory(false) 两次然后相减,结果波动大、不可靠。关键在强制触发 GC 并抑制回收干扰:
- 先调用
GC.Collect()+GC.WaitForPendingFinalizers(),清空待回收对象 - 再调用一次
GC.Collect(),确保代0/1/2都尽可能回收(尤其注意:.NET 6+ 默认启用背景 GC,GC.Collect()不保证立刻完成,加GC.WaitForFullGCComplete()更稳妥) - 记录
GC.GetTotalMemory(true)作为基线(true表示强制算准,会暂停线程) - 运行目标代码
- 再次调用
GC.GetTotalMemory(true),差值即为该段代码新增的托管内存(单位字节)
GC.GetTotalMemory 返回值为什么忽高忽低?
它只统计托管堆已分配但尚未回收的字节数,不包括栈内存、本机资源(如 FileStream 底层句柄)、JIT 编译代码、类型元数据等。而且:
- 未触发 GC 时,已标记为可回收的对象仍计入总数,导致虚高
- 大对象堆(LOH)回收频率低,短期大量
new byte[1024*1024*10]后不 GC,差值会严重失真 - 多线程下,其他线程同时分配内存也会被计入,必须在单线程可控环境测量
- .NET Core/.NET 5+ 中,
GC.GetTotalMemory在容器环境下可能受 cgroup 限制影响,返回值 capped
想看实时内存分布?别只盯 GC.GetTotalMemory
它给不出对象类型、引用链、泄漏线索。真要定位问题,得配合其他手段:
- 用 Visual Studio 的“调试 → 性能探查器 → .NET 内存分配”录制,能精确到每行代码分配了多少
string、List - dotnet-dump +
dumpheap -stat查看生产进程当前托管堆各类型实例数和总大小 - 对疑似泄漏对象,用
!gcroot(在 WinDbg 或 dotnet-dump 中)追根溯源,看谁还在引用它 -
GC.CollectionCount(2)可检查是否频繁触发 Gen2 GC,这是内存压力的重要信号
为什么不用 Process.WorkingSet64 替代?
它返回的是进程整个工作集(含托管堆、本机堆、DLL 映像、共享内存),噪声极大。比如你只是加载了一个新 Assembly,它就跳涨几十 MB,但这和你的代码逻辑无关。
更麻烦的是:Windows 会把部分页面换出到页文件,WorkingSet64 可能突然缩水;而 Linux 下 RSS 值又受 overcommit 和 cgroup 限制扭曲。它适合看“进程吃不吃得消”,不适合分析“这段 for 循环到底造了多少对象”。
真正难的从来不是读一个数字,而是确认这个数字到底代表什么——托管堆增长、LOH 碎片、非托管泄漏、还是 GC 策略本身在捣鬼。











