托管(.net)应用程序中内存泄漏的最常见(且经常被忽略)原因是什么? [英] What are the most common (and often overlooked) causes of memory leaks in managed (.net) applications?

查看:79
本文介绍了托管(.net)应用程序中内存泄漏的最常见(且经常被忽略)原因是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

任何人都可以推荐一份快速检查表/最佳实践指南,以帮助我们避免可能导致.net应用程序中内存泄漏的简单(但细微)错误

当我处于项目的测试阶段时,我很难开始寻找导致内存泄漏的原因.

如果有经验法则"可以完全指导托管应用程序中的内存泄漏,请恳请您分享经验.

谢谢.

(我认为托管应用程序应该是内存托管"的,即GC?为什么我们仍然在纯托管代码中发现泄漏?)

解决方案

泄漏的形式很多:

  • 非托管泄漏(分配非托管代码的代码)
  • 资源泄漏(分配和使用非托管资源(如文件,套接字)的代码)
  • 延长的对象寿命
  • 对GC和.NET内存管理工作原理的错误理解
  • .NET运行时中的错误

前两个通常由两个不同的代码段处理:

  • 在对象上实现IDisposposable并使用Dispose方法处理非托管内存/资源
  • 实施终结器,以确保在GC发现符合收集条件的对象时重新分配非托管资源

但是,第三个是不同的.

假设您正在使用一个包含数千个对象的大列表,总计内存量很大.如果您对这个列表的引用停留的时间超出了您的需要,那么您将遇到内存泄漏的情况.另外,如果您继续添加到此列表中,以使其定期增加更多数据,并且永远不会重复使用旧数据,则肯定会发生内存泄漏.

我经常看到的这种方法的一个来源是将方法附加到事件处理程序上,但是在完成后忘记将它们注销,从而使事件处理程序的大小和执行代码缓慢膨胀.

第四,对.NET内存管理工作原理的错误理解可能意味着您在进程查看器中查看内存使用情况,并注意到您的应用程序的内存使用量一直在增长.如果您有很多可用的内存,则GC可能不会经常运行,这会给您有关当前使用内存(而不是映射内存)的错误图片.

第五,更难,到目前为止,我只看到过.NET中的一个资源管理错误,而afaik已计划在.NET 4.0中进行修复,这是将桌面屏幕复制到.NET映像中. /p>


编辑:针对评论中的问题,如何避免保留比必要时间更长的引用,那么唯一的方法就是做到这一点.

让我解释一下.

首先,如果您有一个运行时间较长的方法(例如,它可能正在处理磁盘上的文件,下载某些内容或类似内容),并且在该方法的早期使用了对大数据结构的引用,长时间运行的部分,然后在该方法的其余部分中不使用该数据结构,那么在发行版中(并且不在调试器下运行).NET足够聪明,可以知道此引用,尽管它保留在技术上范围内的变量中,可以进行垃圾回收.垃圾收集器在这方面确实很进取.在调试版本中并在调试器下运行,它将保留方法的生命周期参考,以防您在断点处停止时检查它.

但是,如果引用存储在声明该方法的类的字段引用中,则它不是那么聪明,因为无法确定它是否会在以后重用,或者至少很难重用.如果不需要此数据结构,则应清除所持有的引用,以便GC稍后再使用它.

Please can anyone recommend a quick checklist / best practice guide to help us avoid simple (but subtle) mistakes that cause could cause memory leaks in .net apps

I find it difficult and quite painful to begin searching for the cause of a memory leakage when i'm in the testing phase of a project.

If there are 'rules of thumb' to completely guide against memory leaks in managed applications, i implore you to please share your experience.

Thanks.

(I thought Managed applications are suppose to be 'memory managed' i.e. the GC? Why then do we still find leakages in purely managed code?)

解决方案

There are many forms of leaks:

  • Unmanaged leaks (code that allocates unmanaged code)
  • Resource leaks (code that allocates and uses unmanaged resources, like files, sockets)
  • Extended lifetime of objects
  • Incorrect understanding of how GC and .NET memory management works
  • Bugs in the .NET runtime

The first two is usually handled by two different pieces of code:

  • Implementing IDisposable on the object and disposing of the unmanaged memory/resource in the Dispose method
  • Implementing a finalizer, to make sure unmanaged resources are deallocated when GC has found the object to be eligible for collection

The third, however, is different.

Say you are using a big list holding thousands of objects, totalling a significant size of memory. If you keep around a reference to this list for longer than you need to, you will have what looks like a memory leak. In addition, if you keep adding to this list, so that it grows with more data periodically, and old data is never reused, you definitely have a memory leak.

One source of this I've seen frequently is to attach methods to event handlers but forget to unregister them when you're done, slowly bloating the event handler both in size and code to execute.

The fourth, an incorrect understanding of how .NET memory management works can mean that you look at the memory usage in a process viewer and notice that your app keeps growing in memory usage. If you have lots and lots of memory available, GC might not run that often, giving you an incorrect picture of the current usage of memory, as opposed to mapped memory.

The fifth, that's harder, I've only seen one resource management bug in .NET so far and afaik it has been slated for fix in .NET 4.0, it was with copying the desktop screen into a .NET image.


Edit: In response to the question in the comments, how to avoid keeping references longer than necessary, then the only way to do that is to to just that.

Let me explain.

First, if you have a long-running method (for instance, it might be processing files on disk, or downloading something, or similar), and you used a reference to a big data-structure early in the method, before the long-running part, and then you don't use that data structure for the rest of the method, then .NET, in release-builds (and not running under a debugger) is smart enough to know that this reference, though it is held in a variable that is technically in scope, is eligible for garbage collection. The garbage collector is really aggressive in this respect. In debug-builds and running under a debugger, it will keep the reference for the life of the method, in case you want to inspect it when stopped at a breakpoint.

However, if the reference is stored in a field reference in the class where the method is declared, it's not so smart, since it's impossible to determine whether it will be reused later on, or at least very very hard. If this data structure becomes unnecessary, you should clear the reference you're holding to it, so that GC will pick it up later.

这篇关于托管(.net)应用程序中内存泄漏的最常见(且经常被忽略)原因是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆