Visual Studio如何排查C#内存问题
C# 程序的内存曲线偶尔升高并不等于泄漏,真正值得追的是:同一业务动作执行多次后,对象数量和托管堆持续增长,而且退出页面、清空集合或等待垃圾回收后仍不回落。Visual Studio 的 Memory Usage 可以把这件事拆成可验证的步骤:先拍基线,再重放场景,比较快照,最后沿 Paths to Root 找到仍在持有对象的引用。
实践要点
- 先固定一条可重复的业务路径,不要一上来就盯整个进程的总内存。
- 至少拍两张快照;更稳妥的做法是基线、第一次操作、第二次操作各拍一张。
- 优先看
Objects (Diff)与Heap Size (Diff)连续增长的类型。 Paths to Root才能回答对象为什么还活着,单看对象大小不能直接判定泄漏。- 修改代码后必须用同一条操作路径复测,确认差值不再逐轮累积。
步骤一:在 Visual Studio 打开 Memory Usage
先打开要排查的 C# 项目,把问题场景缩小到一条能重复执行的路径,例如“进入订单详情—返回列表”“打开窗口—关闭窗口”或“刷新数据—清空结果”。在场景开始前和结束后各设置一个断点,避免初始化、登录和后台任务的分配噪声混进结果。
启动调试后,按菜单路径 Debug > Windows > Show Diagnostic Tools 打开诊断窗口;如果窗口已经显示,直接切到 Memory Usage。在 Summary 区域能看到 Take Snapshot,说明内存快照工具已经进入可用状态。

上图是微软官方 Visual Studio 界面截图。验收点不是进程内存曲线有没有波动,而是诊断窗口中已经出现 Memory Usage 和 Take Snapshot。如果没有看到这些项,先确认项目已进入调试状态,并检查诊断工具是否被关闭。
步骤二:按同一场景拍三张内存快照
程序停在第一个断点时,点击 Take Snapshot 拍基线。接着按 F5 执行一次问题场景,在结束断点再次拍快照;然后回到初始状态,用完全相同的输入再执行一次,并拍第三张快照。
三张快照的意义不同:
- Snapshot 1:场景开始前的基线。
- Snapshot 2:完成一次业务操作后的变化。
- Snapshot 3:重复同一操作后的变化,用来判断对象是否逐轮累积。

成功状态是列表中出现多行快照,并显示 Objects (Diff) 和 Heap Size (Diff)。这里先别急着把红色上箭头当成泄漏。页面首次打开、JIT 编译、缓存预热和一次性元数据加载都可能让第二张快照增长;更有价值的是第二、第三次重复后,同一批类型仍按相近幅度上涨。
步骤三:从 Objects Diff 找到持续增长的类型
点击某张快照的 Objects (Diff) 数字,可以按对象数量差异打开报告;点击 Heap Size (Diff) 则更适合找单个对象不多、但占用字节明显的类型。排查 C# 托管内存时,先观察下面四列:
| 界面字段 | 它回答的问题 | 判断方式 |
|---|---|---|
| Count | 这种对象还活着多少个 | 重复场景后是否持续增加 |
| Size (Bytes) | 对象自身占用多少托管内存 | 适合识别大量小对象 |
| Inclusive Size | 对象连同其引用对象共占多少内存 | 适合发现持有大对象图的入口 |
| Compare With Baseline | 当前报告与哪张快照比较 | 确保比较的是相邻业务阶段 |
可以在 Filter types 中输入自己项目的命名空间,例如 OrderClient.,先排除框架初始化对象。若一个 OrderViewModel 在每次“进入—退出”后都增加,而窗口已经关闭,这才值得继续追引用链。

选中可疑类型后,查看下方的 Paths to Root。它展示从该对象到垃圾回收根的持有链。只要链上仍有静态字段、长生命周期服务、事件发布者、计时器或任务回调引用对象,垃圾回收器就不能释放它。上图中上半区是类型数量和大小,下半区就是用于追根引用的界面。
步骤四:结合 Paths to Root 和 Insights 判断原因
常见 C# 内存问题通常不是“垃圾回收器失效”,而是代码仍然持有引用。看到引用链后,可以按持有者类型缩小范围:
- 事件发布者长期存活:订阅对象离开页面时没有执行
-=,发布者继续持有订阅者。 - 静态集合不断加入:
static List、字典或缓存只有添加,没有淘汰和容量边界。 - Timer 或回调未释放:计时器、注册回调、后台任务闭包捕获了窗口或 ViewModel。
- 大对象被间接持有:一个看似很小的管理对象通过字段引用了大数组、图片字节或完整结果集。
在 Managed Memory 报告中切换到 Insights,Visual Studio 还会按当前快照给出可用的自动分析项,例如 Duplicate Strings、Sparse Arrays,以及部分版本和场景下的事件处理器泄漏提示。它适合帮助缩小方向,但不能替代引用链和代码检查。

上图的可见验收点是 Insights 页已经列出具体类型、浪费字节和数量。比如重复字符串很多,优化方向可能是减少重复构造或调整缓存;稀疏数组占用明显,则要检查是否用过大的数组承载少量元素。不要看到建议就立刻大改,先回到自己的类型和调用路径验证。
修复后如何确认内存问题真的解决
修复事件退订、集合清理、缓存淘汰、IDisposable.Dispose 或计时器停止逻辑后,重新启动同一调试会话,并重复完全相同的输入与操作次数。验收时重点看三个结果:
- 第二轮、第三轮的 Objects (Diff) 不再按固定数量累积。
- 可疑业务类型的 Paths to Root 不再指向旧页面、旧 ViewModel 或已经结束的任务。
- 一次性缓存增长后趋于稳定,而不是每执行一次就增加一份。
如果对象数量已经稳定,但进程工作集没有立刻下降,也不要据此判断修复失败。.NET 运行时可能保留已提交内存供后续分配使用。此时更应该看托管堆快照中的存活对象和差异,而不是只看任务管理器里的进程内存。
常见问题
为什么每次快照都会多出一些对象?
调试器、诊断工具、JIT、日志和缓存都可能产生额外对象。把场景做小、设置前后断点并重复两到三次,可以把一次性噪声与持续累积分开。
Objects Diff 增长但 Heap Size Diff 很小,需要处理吗?
要结合对象类型判断。大量短小对象可能只增加少量字节,但如果它们代表已关闭窗口、请求上下文或事件订阅者,仍然说明生命周期有问题。
Heap Size Diff 很大,但对象数量变化不多是什么原因?
通常要检查大数组、字符串、图片字节、缓存结果或某个对象持有的大对象图。此时 Inclusive Size 比单纯的 Count 更有参考价值。
应该用调试器里的 Memory Usage,还是 Performance Profiler?
需要断点、单步和在特定代码位置拍快照时,用调试器集成的 Memory Usage 更直接;要分析 Release 构建、减少调试器影响或做更完整的性能采集时,可以从 Debug > Performance Profiler 启动 Memory Usage。
排查结果整理
Visual Studio 排查 C# 内存问题的关键不是拍一张快照后找“最大的类型”,而是控制变量:用同一场景建立基线,比较连续快照,锁定持续增长的业务类型,再沿 Paths to Root 找到真正持有它的对象。修复后按原路径复测,差值不再逐轮累积,才算完成一次可靠的内存问题闭环。
Python asyncio.Semaphore 控制多模型并发:避免推理请求同时打满显存
- 上一篇
- Python asyncio.Semaphore 控制多模型并发:避免推理请求同时打满显存
- 下一篇
- Java CharsetDecoder 怎么定位乱码:REPORT 模式、ByteBuffer 边界与解码结果
-
- 文章 · 软件教程 | 1小时前 | 网络安全 · firefox · 软件教程 · 网站证书 · Firefox页面信息 查看网站证书 Firefox安全连接 View Certificate
- Firefox 如何用页面信息查看网站证书:从安全连接打开证书详情
- 250浏览 收藏
-
- 文章 · 软件教程 | 9小时前 | github · actions · 软件教程 · GitHub Actions 失败任务 重新运行工作流
- GitHub Actions 如何重新运行失败任务:Run workflow 页面与作业状态核对
- 412浏览 收藏
-
- 文章 · 软件教程 | 9小时前 |
- GitHub 网页端怎么查看一次提交的完整改动:提交页、文件列表与差异核对
- 424浏览 收藏
-
- 文章 · 软件教程 | 11小时前 | devtools · firefox · 响应式设计 · Firefox响应式设计模式 移动端断点 DevTools截图
- Firefox 如何用响应式设计模式检查移动端断点:设备尺寸与截图导出
- 471浏览 收藏
-
- 文章 · 软件教程 | 14小时前 |
- GitHub Releases 如何核对版本标签与发布资产
- 131浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- ljg-skills
- ljg-skills 是李继刚开源的 AI 技能与提示词集合,面向大模型使用者整理了一批可复用的 prompt、角色设定和任务技能模板,适合用于学习提示词设计、搭建个人 AI 工作流和沉淀团队常用智能体能力。
- 5433次使用
-
- MELO音乐
- MELO音乐是一站式AI视频与音乐制作助手,对标suno, udio的高品质体验。提供伴奏生成、原创写词、无损导出、哼唱识曲、混音变声等全套音频与短视频编辑工具。无论是流行Kpop、电音说唱、民谣古风、摇滚儿歌还是商用轻音乐,MELO为你免费谱曲,轻松做同款!
- 4917次使用
-
- UniScribe
- UniScribe 是一款 AI 音视频转文字与内容整理工具,支持上传音频、视频文件或粘贴 YouTube 链接,自动生成转写文本、摘要、思维导图和关键问题,并支持多格式导出,适合会议记录、课程学习、访谈整理和内容创作复盘。
- 4838次使用
-
- 剧云
- 剧云是专业中文剧本创作平台,安全稳定运行十余年,集成AI编剧、剧本医生审核、人物小传、剧情关系图、大纲编写、多人协作、Word导入导出、版权管控功能,数据安全防护,轻松高效创作剧本。
- 5103次使用
-
- 万象有声
- 万象有声,一个专为有声创作者打造的新一代智能有声内容创作平台。平台提供专业的智能拆章、智能画本编辑、AI配音、AI生成音效、后期制作、智能对轨、智能审听等有声创作全流程工具,可以帮助创作者高效、低成本创作出引人入胜的有声作品。立即体验,让有声书制作更简单!
- 5058次使用
-
- 异步编程的应用:什么情况下会用到异步编程
- 2023-02-16 337浏览
-
- 关于这几种特殊情况的文件对比算法
- 2023-02-24 184浏览
-
- 设置cookie之后,刷新页面消失
- 2023-01-08 488浏览
-
- 文本中含有'这样的符号乱码,如何将其转为正常显示的文本?
- 2023-02-24 259浏览
-
- 接口返回的HTTP状态码为201,这个是怎么实现的?
- 2023-01-25 233浏览

