如何避免内存泄漏?

我可以使用哪些提示来避免应用程序中的内存泄漏? 我有什么可以留意的陷阱或陷阱吗?

在IDisposable对象上调用Dispose或使用using子句。 这应该照顾我能想到的大部分泄漏。

注意您删除了您使用的任何事件处理程序。 在.NET中,它们是泄漏内存的最常见原因。

正如ocdecio所提到的,请确保在Idisposable对象上调用Dispose,并记住在完成对象时删除事件处理程序。 在构建使用非托管资源的类时,请务必实现Idisposable,以便用户知道有一些关键资源需要处理。

此外,即使垃圾收集为您做了相当多的工作,您也应该摆脱对您完成的对象的引用。 否则他们仍然会有一个根,他们将不会被GC。

在这些情况下,不要低估工具的帮助性。 .NET内存分析器现在相当成熟和强大,如果你有一个复杂的应用程序,你认为应该释放的对象仍被其他东西作为参考,那么精确定位该引用的能力是非常宝贵的。

我刚刚完成了内存泄漏,其中一个WPF标签页托管了一个Windows窗体控件(因为这些标签保存了大量数据,您可以随意打开和关闭它们,只需等待GC清除内存关闭不是一个选项)。 我使用YourKit分析器在打开选项卡之前获取内存的快照,打开选项卡,再次关闭它并拍摄另一个快照。 在探查器内部,您可以直观地比较两个快照,并查看哪些对象在该过程中幸存,并将其引用关注回GC根。 我没有其他剖析器的经验,但我知道如果YourKit无法满足您的需求,那里有一些。


编辑添加:

好吧,这不是避免内存泄漏,它正在修复它们。 但我会把它留在这里,因为我认为它是有用的信息,我认为没有足够的.NET开发人员知道这些工具。

我知道有些人会建议垃圾收集作为解决方案。 但是有很多情况下垃圾收集不能提供您期望的结果。 很容易最终保持一个杂散的引用,阻止整个内存链被释放。 了解这是如何破坏DARPA Grand Challenge入口的。 您可以认为这些不是内存泄漏,但如果程序希望释放内存,那么它仍然是一个问题。 就像在C编程中一样,几个月后你就会知道如何确保不留下不必要的引用。

内存泄漏是错误,所以一般来说 – 这个问题可以像“如何编写没有错误”一样回答? 从长远来看 – 不可能没有错误,但你可以限制在发布的代码中使用它们的机会。

首先关心开发的代码质量并遵循其他人提到的准则。

  1. 简单性是金色的 – 代码越简单 – 错误或泄漏的可能性就越小
  2. 使用非托管资源时要小心–Windows句柄,数据库连接,GDI对象等。
  3. 用于IDisposables
  4. 为承载非托管资源的类实现IDisposable
  5. 确保删除对未使用对象的任何引用 – 包括棘手的事件处理程序

除此之外 – 实现测试以查看内存是否泄漏 – 单元,并发,压力和负载测试最有帮助。 通过检查指标(性能计数器)查看内存是否泄漏。 您还可以记录对象创建和破坏,并在测试结束时分析日志。

我遇到了一个问题,其中一个对象(Ping)实现了两次Dispose(),通过实现IDisposable接口并同时inheritance它。 inheritance的方法什么也没做,因此在调用Dispose()时你必须将对象强制转换为IDisposable,否则会泄漏内存。 这是我几年前写的更详细的post 。

  1. 包裹任何在使用构造中可丢弃的东西。
  2. 避免使用COM对象。
  3. 检查是否正确删除了所有事件处理程序。
  4. 检查是否正确删除了所有数据绑定。
  5. 把事情简单化

如果您的应用程序逻辑变得不必要地复杂,您可能会开始以内存泄漏结束。 如果你保持你的类很小并遵循一般的编码实践,你可能不会遇到托管代码的任何内存泄漏。 这是可能的,但不像它使用的那样可能。

如果您怀疑内存泄漏,请使用分析器查看是否有任何对象被保留的时间超过了所需的时间。

我最后一次遇到严重的内存泄漏是.NET 1.1,结果发现框架中有一个错误。

基本了解垃圾收集器的工作原理将有助于避免滥用内存。 例如,如果要保留对不再需要的对象的引用,则gc将无法收集它。

同样,如果您要存储用户输入的数据,或者随着时间的推移而添加的数据,您应该考虑某种限制,以便您的内存使用量不会无限增长。

我在.NET中遇到的大多数内存泄漏都与使用COM对象有关,而不是正确释放它们。 只要我看到对COM对象的引用,我就认为“内存泄漏”。

最常见的内存未被GC破坏的情况是当你的事件处理程序没有被正确解开时。 如果需要,您可以在Dispose()中取消挂钩。

我更详细地描述了这个问题,我有办法编写测试来确定你是否在这里泄漏了对象。

正如其他人所说的那样调用Dispose()(或使用using语句),但另外考虑类是否使用资源,如果是,则实现IDisposeable。 这通常是我的代码中的问题是我有一个类成员,直到GC才清理。

它是托管代码,即c#,所以你必须努力泄漏内存:P

试试google: http : //www.google.com/search?hl = en&client = firefox-a&rls = org.mozilla%3Aen-US%3Aofficial&hs = & mbp&q = c%23 + memory+leaks&btnG = Search

如果由于某种原因任何一个终结器阻塞,实现终结器的类型可能会泄漏。 我看到终结器由于锁定和线程公寓问题而阻塞。

由于在其各自的终结器运行之前不会收集具有终结器的类型的实例,因此单个阻塞终结器将阻止任何其他可终结的对象等待收集。

首先,让我分享我对内存泄漏的严格理解。 我对内存泄漏的定义是当你已经分配了内存并且不再引用它时,因此无法释放该内存。 话虽如此,在.net对象中存在内存泄漏是不可能的(我的意思是存在于托管堆中的CTS类型的实例)。 未引用的内存正是GC所要求的免费内存。

话虽如此,人们可以对内存泄漏的内容有更多的了解。 如果你认为内存泄漏是所使用的内存不受控制的增长,那么这很容易。 只是大量滥用静态变量,主要是那些引用巨大列表的变量。 如果您保留引用这些对象,GC将永远不会清理它们,将它们推广到更高代,并使它们更难以收集。 即使这不是严格意义上的内存泄漏,但在一天结束时它可能会导致类似的症状。 尝试检测这种“泄漏”的好方法是使用CLR Profiler 。

“泄漏”的另一个原因是如前所述通过不当使用事件处理程序。 每当对象A用对象B的事件注册其实例方法之一时,对象B就会结束对对象A的间接引用,这意味着当B处于活动状态时,A将保持活动状态。 但请注意,这里没有循环。 只要B或A都没有任何根引用,无论它们有多少交叉引用,它们最终都会被收集。

最后,人们实际上可以在.net中引发内存泄漏,但在谈论托管内存时从不(至少在理论上),因为GC在清除它时做得很好。 如果任何对象维护对非托管内存的引用(例如通过interop),则需要显式清除该内存。 事实上,如果不这样做会导致内存泄漏。 即使我从未经历过这样的场景,至少在理论上这可能会发生。 如前所述,持有此类引用的对象应该实现IDiposable以清除该内存,并且它们的使用应该保证为此目的调用Dispose,主要是通过使用using子句。 请注意,Dispose不会释放对象的内存,但只会要求对象释放它所引用的任何非托管内存。

一种特殊的非托管内存是互操作场景中使用的COM对象所需的内存。 这些对象可以通过Runtime Callable Wrappers,RCWs为朋友访问,但没有Dispose。 “使用”不起作用。 释放底层COM对象的方法是通过静态方法:

 System.Runtime.InteropServices.Marshal.ReleaseComObject(object); 

由于“using”只是在finally块中调用IDisposable.Dispose()的语法糖,因此它不能与RCW一起使用,因此不要忘记将自己的调用置于最终块中的ReleaseComObject(对象)中,以便确保它真的叫。

您可能会发现此链接很有用: 解决事件问题:弱事件处理程序 。

使用“using”关键字自动调用IDisposable对象的Dispose()方法。
对于任何COM互操作,您必须手动释放所有资源。

您可能会发现我的新文章很有用: 如何检测并避免.NET应用程序中的内存和资源泄漏