【发布时间】:2009-07-24 23:27:35
【问题描述】:
不久前我的应用使用了 150mb 的内存,现在是 286mb。它慢慢升起,所以我一定是忘记处理一些东西了。这对我来说不是什么大问题,因为我有 4gb,但我想把它发送给只有 1gb 内存的其他人。除了逐行查看代码,我如何才能找到需要处理的对象或通常是大对象?
【问题讨论】:
标签: c# memory-leaks performance
不久前我的应用使用了 150mb 的内存,现在是 286mb。它慢慢升起,所以我一定是忘记处理一些东西了。这对我来说不是什么大问题,因为我有 4gb,但我想把它发送给只有 1gb 内存的其他人。除了逐行查看代码,我如何才能找到需要处理的对象或通常是大对象?
【问题讨论】:
标签: c# memory-leaks performance
扩展 JP 和 Reed 的答案。
我想澄清一点困惑。如果您看到内存显着增加,则该问题不太可能是调用 Dispose 的问题。 Dispose 通常用于释放句柄等非托管资源。这些不会占用太多内存,而是作为资源更宝贵。
内存的增加通常与可从托管对象访问的大型对象或集合相关联,该托管对象通过堆栈对象或强 GC 句柄直接或间接作为根。这是您可能希望重点调查的领域。
【讨论】:
查看.NET Memory Profiler。有 15 天的试用期,值得支付许可费。
通过以下方式轻松识别内存泄漏 收集和比较快照 .NET 内存快照包括数据 关于 .NET 实例分配 和当时的实时实例 快照已收集。他们提供了一个 很多有用的信息,并使它 易于识别潜在记忆 泄漏,尤其是当两个快照 进行比较。
【讨论】:
【讨论】:
您还可以使用 WinDbg 和 SOS。这些具有免费且非常非常彻底的优势,如果有点难以习惯的话。
这里有一个blog post 描述了这个过程。
【讨论】:
代码项目目前有一个指向专门用于查找未处置对象的应用的链接。
【讨论】:
Stephen Toub 详细解释了执行此操作的各种技术, 以下是他文章中的一些简短亮点
通过添加一个用于调试目的的终结器,您可以引入一种方法来找出何时 一个类没有正确处理,如果终结器从未被调用,你知道 处置没有被调用
获取有关实例、threadId 等的其他信息以帮助缩小范围 哪个实例没有被调用,他创建了一个 FinalizationDebgger 类 您的一次性班级将持有哪个,而后者又会调用 Dispose 的 FinalizationDebugger 类实例,当它本身被释放时。如果未调用 Dispose 你的类实例然后当终结器运行时它将调用终结器 FinalizationDebgger 实例,您可以在其中断言或抛出异常以帮助调试 问题,
将所有跟踪相关代码移动到一个基类中,您的一次性类将 然后继承自,这使代码更清晰。这种方法可能有效,也可能无效 因为你烧了一个基类,如果你已经从另一个基类继承了。
在最后一个选项中,所有内容都被分解为您的实例的静态类 呼入。 FinalizationDebugger 变成了一个静态类,暴露了三个静态 方法:构造函数、处置和终结器。这个想法是你调用这些方法 从您班级中的适当位置(处置/最终确定/构造函数)。这是最低限度的 侵入您的代码,因为它通常只涉及添加三行代码。全部 这些方法中有一个用 ConditionalAttribute 标记,因此它们只会被调用 当你在调试模式下编译你的类时由你的类。
Stephen 还解释了他的每种方法的优缺点。 这些解决方案提供了各种选项,您需要对每个选项进行评估,以确定哪一个最适合您的情况。必须阅读恕我直言
希望这会有所帮助。
【讨论】:
也可以试试ANTS Memory Profiler。有 14 天的全功能免费试用,我真的很喜欢这个 UI。
披露:他们现在赞助 Herding Code,这就是我尝试的原因。但我对它印象深刻——我分析了一个应用程序 30 小时并从中获得了大量有用的信息。用户界面真的很有帮助 - 它指导您完成整个过程,而且看起来很糟糕。
alt text http://www.red-gate.com/products/ants_memory_profiler/images/object_retention_graph.gif
【讨论】:
这里有几个技巧使用ANTS Memory Profiler 来帮助找到未处理的对象并修复泄漏。
ANTS 内存分析器允许设置过滤器,仅显示 包含 Dispose() 方法的对象。打开这个,你会 给定一个尚未处理的活动对象列表。
虽然查找未处理的对象很有用,但更有用的是 知道为什么这些对象没有被处理。寻找这些在哪里 创建未处理的对象对找到 泄漏的原因。如果您能够更改泄漏对象的代码,则 有用的技巧是引入一个字段来保存堆栈跟踪,并且 在对象构造时填充此字段。那么因为 ANTS 内存分析器允许您检查对象的字段, 您可以简单地读取堆栈跟踪,就像创建泄漏对象时一样。这会给你一个强有力的线索来判断谁 泄漏对象的所有者应该是,以及他们应该如何对他们负责的对象调用 Dispose。
【讨论】: