最常用且靠谱的是 Process.GetCurrentProcess().WorkingSet64,它返回进程当前实际占用的物理内存字节数(Working Set),与任务管理器“内存(专用工作集)”基本一致,适用于监控、告警等场景,开销低、无需权限、跨.NET版本兼容。

用 Process.WorkingSet64 获取当前进程内存占用(最常用)
直接读取 Process.GetCurrentProcess().WorkingSet64 是多数场景下最靠谱的选择,它返回的是进程当前实际占用的物理内存字节数(即 Working Set),反映真实“被操作系统钉在内存里”的量。
注意:这个值不是 GC 堆大小,也不是虚拟内存,更不是“任务管理器里看到的那个数字”——但和任务管理器的“内存(专用工作集)”列基本一致,可比性强。
- 适用于快速监控、日志打点、阈值告警等常规需求
- 调用开销极低,每秒调用几百次都没压力
- 不需要额外权限,.NET Core 3.0+ 和 .NET Framework 4.0+ 都支持
- 别用
PrivateMemorySize64—— 它包含已提交但未访问的页,在空闲时虚高;也别用VirtualMemorySize64—— 地址空间大小和实际内存无关
GC 堆大小 ≠ 进程内存,别混淆 GC.GetTotalMemory
GC.GetTotalMemory(false) 返回的是托管堆中已分配但尚未回收的对象总字节数,它不包含非托管资源(如图像句柄、文件映射、P/Invoke 分配的内存)、JIT 代码、线程栈、CLR 内部结构等。
常见误用是拿它当“程序用了多少内存”来判断 OOM 风险——结果往往严重低估。比如一个程序加载了 500MB 的 Bitmap(非托管),GC.GetTotalMemory 可能只报 20MB。
-
GC.GetTotalMemory(true)会强制触发一次 GC,影响性能,生产环境禁用 - 它只反映托管堆“申请过多少”,不反映“释放了多少”,也不反映 OS 看到的物理占用
- 若你真正关心的是“对象膨胀是否失控”,它有用;若关心“会不会被系统杀掉”,它几乎没用
跨平台兼容性差的坑:Process.PeakWorkingSet64 在 Linux/macOS 上不准
Windows 下 Process.PeakWorkingSet64 能准确返回进程历史最高物理内存占用;但在 Linux/macOS(通过 dotnet runtime)上,该属性始终返回 0 或一个无意义的固定值——因为底层没有对应接口映射。
如果你写的是跨平台服务,并依赖峰值内存做自动扩缩容或诊断,这里会静默失效。
- Linux 下可用
/proc/self/status解析VmRSS字段替代,但需System.IO.File.ReadAllText+ 字符串匹配,且仅限 Linux - macOS 没有稳定等效字段,
task_infoAPI 需要 unsafe 代码和权限,不推荐 - 统一方案?放弃
PeakWorkingSet64,改用周期采样 + 自行记录最大值(用WorkingSet64)
高频采集时注意 GC 干扰和精度抖动
每毫秒调用一次 WorkingSet64 不会崩溃,但可能因 OS 内存调度(如页面换入换出、Working Set trimming)导致数值跳变几十 MB,看起来像内存泄漏。
这不是 bug,是 Windows 内存管理的正常行为:空闲物理内存充足时,OS 会放宽 Working Set;紧张时主动回收,数值骤降。
- 监控建议间隔 ≥ 500ms,避免噪音干扰判断
- 不要单看瞬时值,重点观察趋势(10 秒滑动平均)、突增幅度(如 2 秒内涨 300MB)
- 若配合 GC 日志分析,可加一句
GC.CollectionCount(2)判断是否刚发生 Gen2 GC,帮助解释内存回落
真正难的不是读哪个数,而是理解每个数背后代表的操作系统语义——同一行代码,在 Windows/Linux 上返回的其实是不同维度的指标,强行统一口径反而误导诊断。


















