C# .NET运行时如何按类/对象类型统计应用内存占用?
你不需要付费类库,直接用微软官方免费开源的Microsoft.Diagnostics.Runtime(即ClrMD)就能在代码内部直接生成和SOS扩展dumpheap -stat格式完全一致的堆内存统计,不需要附加远程调试器,完全适配Heroku这类云平台部署场景。
该方法生成的输出和你预期的效果完全对齐:
dumpheap -stat
Statistics:
MT Count TotalSize Class Name
...
00007f6c1eeefba8 576 59904 System.Reflection.RuntimeMethodInfo
00007f6c1dc021c8 1749 95696 System.SByte[]
00000000008c9db0 3847 116080 Free
00007f6c1e784a18 175 128640 System.Char[]
00007f6c1dbf5510 217 133504 System.Object[]
00007f6c1dc014c0 467 416464 System.Byte[]
00007f6c21625038 6 4063376 testwebapi.Controllers.Customer[]
00007f6c20a67498 200000 4800000 testwebapi.Controllers.Customer
00007f6c1dc00f90 206770 19494060 System.String
Total 428516 objects
具体实现步骤
- 首先在项目中安装免费NuGet包:
Microsoft.Diagnostics.Runtime,该包由.NET官方维护,无商用限制,无任何费用 - 嵌入以下内存统计代码,可在任意业务逻辑、监控接口、异常触发逻辑中调用,采样时仅会做极短时间的运行时快照,不会长时间阻塞业务请求
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Microsoft.Diagnostics.Runtime; public static class HeapStatGenerator { public static string GetDumpHeapStatOutput() { int currentPid = Process.GetCurrentProcess().Id; var stats = new Dictionary<ClrType, (long Count, ulong Size)>(); // 以被动模式附加到当前进程,不会触发调试器暂停 using var dataTarget = DataTarget.AttachToProcess(currentPid, attachTimeout: 5000, attachFlag: AttachFlag.Passive); ClrInfo runtimeInfo = dataTarget.ClrVersions[0]; using ClrRuntime runtime = runtimeInfo.CreateRuntime(); // 遍历托管堆所有对象做维度统计 foreach (ClrObject obj in runtime.Heap.EnumerateObjects()) { if (!obj.IsValid || obj.Type == null) continue; ClrType type = obj.Type; if (!stats.ContainsKey(type)) stats[type] = (0, 0); stats[type] = (stats[type].Count + 1, stats[type].Size + obj.Size); } // 按总占用大小升序排序,和原生dumpheap -stat输出顺序一致 var orderedStats = stats.OrderBy(kv => kv.Value.Size).ToList(); // 拼接格式化输出 var outputLines = new List<string> { "Statistics:", $"{"MT",18} {"Count",10} {"TotalSize",12} Class Name" }; long totalObjects = 0; foreach (var entry in orderedStats) { totalObjects += entry.Value.Count; outputLines.Add($"{entry.Key.MethodTable:X16} {entry.Value.Count,10} {entry.Value.Size,12} {entry.Key.FullName}"); } outputLines.Add($"Total {totalObjects} objects"); return string.Join(Environment.NewLine, outputLines); } }
- 调用
HeapStatGenerator.GetDumpHeapStatOutput()即可拿到格式化好的统计结果,建议将该方法封装到仅管理员可访问的内部监控接口中,当内存占用超过阈值时直接调用接口拉取统计数据,快速定位占内存过高的类型。
部署使用注意事项
- Heroku Linux运行环境默认开放进程自身的诊断数据读取权限,不需要额外配置系统参数或dyno权限
- 遍历全堆统计有毫秒级性能开销,不要在核心请求链路高频调用,建议配置为内存告警触发时采样、或每1-2小时低峰期采样一次即可
- 如果需要进一步定位泄漏根,可以基于同一个库扩展实现对象引用链遍历,不需要下载全量dump文件到本地分析,进一步降低云环境下的排查成本。
内容的提问来源于stack exchange,提问作者adamsfamily

