2011-02-16 141 views
26

我想了解内存的概念泄漏更好的任何人都可以点一些有用的信息,这将有助于我更好地理解什么内存泄漏是,我如何才能找到他们在我的代码。内存泄漏C#

谢谢

+0

Windows注册表→搜索→“HeapLeakDetection” – Bitterblue 2013-11-08 14:42:55

+0

这是我刚刚在一篇关于.Net内存泄漏的博客文章:http://crazorsharp.blogspot.com/2009/03/net-memory-leaks-it-is -possible.html – BFree 2011-02-16 18:59:43

回答

3

当你的程序动态地分配不你使用它完成后得到正确释放的内存时发生内存泄漏。如果你有一个程序持续这样做,你的泄漏会变得越来越大,很快你的程序占用你所有的RAM。

10

一个非常良好的阅读是Everybody thinks about garbage collection the wrong way

通常,内存泄漏,或任何资源泄漏,是每当程序分配内存(或任何其它资源),然后省略当完成它释放它。在本机应用程序中,内存泄漏是最常见的资源泄漏,当资源引用(指向已分配块的指针)超出作用域并被销毁时,可能会发生内存泄漏,但分配的资源(内存块)不会被破坏。在这种情况下,资源(内存)被泄漏,因为程序已经失去了释放它的能力,即使它想要,因为它不再记住资源的位置(块的地址)。

在管理应用程序的内存泄漏是有点棘手。由于运行时可以自动跟踪对资源的引用,因此它还可以理解资源(对象)何时不再被应用程序的任何活动部分引用(在任何线程中,没有从堆栈帧引用该资源的链)因此运行时可以理解何时可以安全地收集应用程序不再引用的对象。因此,在被管理的世界中,如果您认为应用程序不再引用对象(因此它可以由运行时收集),但是实际上通过某些引用链,您可以在时引用“泄漏”它因此无法收集。

我强烈建议Raymond Chen的文章上面链接,是非常非常照明。

22

传统的内存泄漏,当您分配内存,然后以某种方式“忘记”释放它发生。在旧的C++代码中,这意味着调用new而没有相应的delete。在C中,这意味着拨打alloc()/malloc()而没有相应的free()

在.Net中,您不会因传统意义上的内存泄漏,因为您不应该自行释放内存。相反,你依靠垃圾收集器为你释放它。但是,这并不意味着你永远不会忘记记忆。有几种方法可能会意外地保留一个引用,以防止垃圾收集器完成它的工作。这些包括全局变量(特别是列表,字典和可能用于“缓存”对象的其他对象),挂在内存上的事件处理程序,递归历史记录引用以及大对象堆。

在这里还要注意,很重要的一点是,仅仅因为您在.Net中看到增加的内存使用模式,它并不一定意味着您的应用程序正在泄漏内存。在整体内存压力较低的情况下,垃圾收集器可能会选择通过不收集,或者通过收集但不将内存返回给操作系统来节省时间。

36

存在多种内存泄漏,但通常这个术语指的是某种不再使用的资源,但仍占用内存。如果你的应用程序中有许多需要大量内存,并最终耗尽了它。

在C#,这些都是一些常见内存泄漏:

  • 不删除事件侦听器。任何使用引用外部对象的匿名方法或lambda表达式创建的事件侦听器都会使这些对象保持活动状态。请记住在事件监听器不再使用时删除事件监听器。
  • 保持数据库连接或结果集未使用时处于打开状态。请记住致电Dispose()所有IDisposable对象。 Use the using statement
  • 使用p/Invoke调用C函数,分配内存然后永远不会释放。
4

应将“内存泄漏”定义为“当您认为不应该使用的内存”时,将其作为C#/ Java应用于垃圾收集语言/运行时。

传统上,“内存泄漏”被定义为未正确释放的内存(请参阅其他答案中的维基百科链接),这对于垃圾收集环境通常不会发生。请注意,由于运行时问题,即使垃圾收集语言可能会泄漏内存 - 即使JavaScript是垃圾收集语言,也很容易在Internet Explorer的JavaScript运行时中泄漏大量JavaScript对象。

5

将内存分配给应用程序时,应用程序有义务将该内存释放回操作系统,以便其他应用程序可以重新使用该内存。当应用程序不释放内存时会发生内存泄漏,从而阻止重新分配内存。

对于托管代码,垃圾回收器跟踪对由应用程序创建的对象的引用。对于大多数情况,CLR将以代理正在运行的进程的合理方式透明地处理内存分配和释放。然而,.NET开发人员仍然需要考虑资源管理,因为即使垃圾收集器工作仍然存在内存可能泄漏的情况。

考虑下面的代码:

Widget widget = new Widget();

上面的代码行创建Widget类的新实例和小部件字段被分配给该对象的引用。 GC跟踪与每个对象相关的引用,并取消分配没有强引用的对象的内存。

值得一提的是,CLR的垃圾收集只会收集托管对象,.NET代码可以并经常使用不能自动垃圾收集的非托管资源。

当分配了这些资源的对象无法在对这些资源的最后一次引用超出范围之前正确地释放它们时,会发生非托管资源泄漏,这会导致分配资源,但未重新引用并因此对应用程序不可用。

直接引用非托管资源的类应确保正确释放这些资源。这样的例子会是这个样子:

public void ManagedObject : IDisposable 
{ 
    //A handle to some native resource. 
    int* handle; 

    public ManagedObject() 
    { 
     //AllocateHandle is a native method called via P/Invoke. 
     handle = AllocateHandle(); 
    } 

    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    private void Dispose(bool disposing) 
    { 
     if (disposing) 
     { 
      //deal with managed resources here 
      FreeHandle(handle); 
     } 
    } 

    ~ManagedType() 
    { 
     Dispose(false); 
    } 
} 

disposing参数是假的终结被调用时。这是为了防止在终结器中使用管理资源,因为在该阶段管理引用应被视为无效。

还要注意,Dispose()方法调用GC.SuppressFinalize(this),这会阻止终结器在该实例中运行。这样做是因为在终止器中释放的资源在Dispose调用中被释放,从而不需要fializer调用。

,使得使用的是处理非托管资源(或者说实现了IDisposable的任何类)类应在 using块内这样做,以确保当不再需要访问资源的 IDisposable.Dispose称为这将需要

客户端代码关心托管和非托管资源,并且在上面的例子中,确保不会对终结器进行非常昂贵的调用。

散步的appoligies。我现在就停下来。