当前位置: 首页 > 编程语言 > C#

如何避免内存泄漏?分享

时间:2023-04-10 18:46:19 C#

如何避免内存泄露?我可以使用哪些技巧来避免应用程序中的内存泄漏?有没有我可以提防的陷阱或隐患?在IDisposable对象上调用Dispose或使用using子句。这应该可以解决我能想到的大多数漏洞。请注意,您删除了您使用的所有事件处理程序。在.NET中,它们是内存泄漏的最常见原因。正如ocdecio提到的,确保在Idisposable对象上调用Dispose,并记住在完成对象后删除事件处理程序。在构建使用非托管资源的类时,一定要实现Idisposable,以便用户知道有一些关键资源需要处理。此外,您应该摆脱对已完成对象的引用,即使垃圾收集为您做了很多工作。否则他们仍然有一个根并且他们不会被GC。在这些情况下,不要低估工具的帮助。.NET内存分析器现在非常成熟和强大,如果您有一个复杂的应用程序,其中您认为应该释放的对象仍然被其他东西引用,那么查明该引用的能力是非常宝贵的。我刚刚完成了一个内存泄漏,其中一个WPF选项卡托管了一个Windows窗体控件(因为这些选项卡包含大量数据,您可以随意打开和关闭它们,只是等待GC清除内存关闭不是一个选项).我使用YourKitprofiler在打开选项卡之前拍摄内存快照,打开选项卡,再次关闭它并拍摄另一个快照。在探查器中,您可以直观地比较两个快照并查看哪些对象在该过程中幸存下来并跟踪它们的引用返回到GC根。我没有使用其他分析器的经验,但我知道如果YourKit不能满足您的需求,也有一些分析器。编辑添加:好吧,它不是在避免内存泄漏,而是在修复它们。但我将其保留在这里,因为我认为它是有用的信息,而且我认为.NET开发人员对这些工具的了解还不够多。我知道有些人会建议将垃圾收集作为一种解决方案。但在很多情况下,垃圾回收并不能提供您期望的结果。很容易最终持有一个杂散的引用,从而阻止释放整个内存链。了解这是如何破坏DARPAGrandChallenge入口的。您可以认为这些不是内存泄漏,但如果程序希望释放内存,那么它仍然是一个问题。就像在C编程中一样,几个月后您就会知道如何确保不留下不必要的引用。内存泄漏是错误,所以一般来说-这个问题可以像“如何没有错误地编写”这样回答?从长远来看——没有错误是不可能的,但你可以限制在发布的代码中使用它们的机会。首先关心开发的代码质量,遵循别人提到的准则。简单是金-代码越简单-错误或泄漏的可能性就越小使用非托管资源时要小心-Windows句柄、数据库连接、GDI对象等。对于IDisposables为托管非托管资源的类实施IDisposable确保对未使用对象的任何引用被删除-包括棘手的事件处理程序除其他外-实施测试以查看是否存在内存泄漏-单元、并发、压力和负载测试最有帮助。通过检查指标(性能计数器)来检查内存泄漏。您还可以记录对象的创建和销毁,并在测试结束时分析日志。我遇到了一个问题,其中对象(Ping)通过实现IDisposable接口并同时继承它来实现Dispose()两次。继承的方法什么都不做,所以在调用Dispose()时必须将对象强制转换为IDisposable,否则会泄漏内存。这是我几年前写的更详细的帖子。包装using构造中可丢弃的任何内容。避免使用COM对象。检查所有事件处理程序是否已正确删除。检查是否正确删除了所有数据绑定。保持简单如果您的应用程序逻辑变得不必要地复杂,您可能开始以内存泄漏告终。如果您保持类的小型化并遵循一般的编码实践,您可能不会遇到托管代码的任何内存泄漏。有可能,但不像以前那么可能了。如果您怀疑存在内存泄漏,请使用探查器查看是否有任何对象的保留时间超过了必要的时间。我上一次出现严重的内存泄漏是在.NET1.1中,结果发现框架中存在错误。基本了解垃圾收集器的工作原理将帮助您避免滥用内存。例如,如果你想保留对不再需要的对象的引用,GC将无法收集它。同样,如果您要存储用户输入的数据或随着时间的推移添加的数据,您应该考虑某种限制,这样您的内存使用量就不会无限增长。我在.NET中遇到的大多数内存泄漏都与使用COM对象而不是正确释放它们有关。只要我看到对COM对象的引用,我就会想到“内存泄漏”。内存未被GC损坏的最常见情况是您的事件处理程序未正确展开。如果需要,您可以在Dispose()中取消挂钩。我更详细地描述了问题,我有一种方法可以编写测试来确定您是否在此处泄漏对象。正如其他人所说的那样调用Dispose()(或使用using语句),但还要考虑该类是否使用该资源,如果是,则实现IDisposeable。这通常是我的代码中的问题是我有一个直到GC才被清理的类成员。它是托管代码,即c#,因此您必须努力工作才能泄漏内存:P试试谷歌: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将永远不会清理它们,将它们提升到更高的世代,并使它们更难收集。尽管这不是严格意义上的内存泄漏,但最终它会导致类似的症状。尝试检测此类“泄漏”的一个好方法是使用CLRProfiler。“泄漏”的另一个原因是由于不正确地使用了前面提到的事件处理程序。每当对象A向对象B的事件注册其实例方法之一时,对象B结束其对对象A的间接引用,这意味着当B处于活动状态时,A将保持活动状态。但是请注意,这里没有循环。只要B和A都没有任何根引用,无论它们有多少交叉引用,它们最终都会被收集。最后,在.net中实际上可能导致内存泄漏,但在谈论托管内存时永远不会(至少在理论上),因为GC可以很好地清理它。如果任何对象维护对非托管内存的引用(例如通过互操作),则需要显式清除该内存。事实上,不这样做会导致内存泄漏。尽管我从未经历过这种情况,但至少在理论上这可能会发生。如前所述,持有此类引用的对象应实现IDiposable以清除该内存,并且它们的使用应保证为此目的调用Dispose,主要是通过使用using子句。请注意,Dispose不会释放对象的内存,而只会要求对象释放它引用的任何非托管内存。一种特殊的非托管内存是互操作场景中使用的COM对象所需的内存。这些对象可以通过RuntimeCallableWrappers,朋友的RCW访问,但没有Dispose。“使用”不起作用。释放底层COM对象的方法是通过静态方法:System.Runtime.InteropServices.Marshal.ReleaseComObject(object);由于“使用”只是在finally块中调用IDisposable.Dispose()的语法糖,它不能与RCW一起使用,所以不要忘记在final块中将你自己的调用放在ReleaseComObject(object)中,这样你就可以确定它真的被调用了。您可能会发现此链接很有用:故障排除事件:弱事件处理程序。使用“using”关键字自动调用IDisposable对象的Dispose()方法。对于任何COM互操作,您必须手动释放所有资源。您可能会发现我的新文章很有用:如何检测和避免.NET应用程序中的内存和资源泄漏C#学习教程:如何避免内存泄漏?如果所有分享的内容对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处: