在.NET等托管环境中是否可能发生内存泄漏?

时间:2011-04-15 10:25:11

标签: c# .net memory-management memory-leaks

在C ++中,很容易发生永久内存泄漏 - 只需分配内存而不释放内存:

new char; //permanent memory leak guaranteed

并且该内存在堆的生命周期内保持分配(通常与程序运行时的持续时间相同)。

在C#程序中是否可以(在内存管理机制正常工作的情况下导致特定的未引用对象从未发布的情况)?

我仔细阅读了this question并回答了它并且它提到了一些导致内存消耗高于预期的情况,或IMO相当极端的情况,例如死锁终结器线程,但是可能会形成永久性泄漏一个具有正常运行的内存管理的C#程序?

7 个答案:

答案 0 :(得分:25)

这取决于您如何定义内存泄漏。在非托管语言中,我们通常将内存泄漏视为已分配内存的情况,并且不存在对它的引用,因此我们无法将其释放。

在.NET中创建这种泄漏几乎是不可能的(除非你调用非托管代码,或者除非运行时有错误)。

但是,您可以获得另一种“弱”形式的泄漏:当存储的引用时(因此仍然可以找到并重置引用,允许GC释放通常是内存),但是思想它没有,所以你假设被引用的对象会得到GC。这很容易导致内存消耗的无限增长,因为你正在堆积对不再使用的对象的引用,但是它们不能被垃圾收集,因为它们仍然在应用程序的某处被引用。

因此,通常认为.NET中的内存泄漏只是您忘记您有对象引用的情况(例如,因为您未能取消订阅事件)。但是参考文献存在,如果你记得它,你可以清除它,泄漏就会消失。

答案 1 :(得分:2)

如果你愿意,你可以在.NET中编写非托管代码,你用不安全的关键字包含你的代码块,所以如果你正在编写不安全的代码,你不会回到自己管理内存的问题,如果没有得到一个内存泄漏?

答案 2 :(得分:1)

这不完全是内存泄漏,但如果您直接与硬件驱动程序通信(即不通过一组驱动程序的正确编写.net扩展名),那么将硬件置于以下状态是相当可能的:虽然您的代码中可能存在或可能没有实际的内存泄漏,但您无法再重新启动硬件或PC而无法访问硬件......

不确定这是否对您的问题有用,但我觉得值得一提。

答案 3 :(得分:1)

当对引用的分析表明内存无法访问时,GC通常会将无法访问的内存的收集延迟到以后的某个时间。 (在某些受限制的情况下,编译器可以帮助GC并警告它在内存区域变为无法访问时。)

根据GC算法,一旦运行收集周期就会检测到无法访问的内存,或者在一定数量的收集周期内它可能无法检测到(例如,代GC会显示此行为)。有些技术甚至有从未收集的盲点(例如使用引用计数指针) - 有些人否认GC算法的名称,它们可能不适用于通用目的。

证明将回收特定区域取决于算法和内存分配模式。对于像标记和扫描这样的简单算法,很容易给出一个约束(直到下一个收集周期),对于更复杂的算法,问题更复杂(在使用动态数代的方案下,条件是完全收集对于不熟悉算法细节和使用的精确启发式的人来说没有意义。

答案 4 :(得分:0)

一个简单的答案是在GC环境中经典的内存泄漏是不可能的,因为经典的内存泄漏被泄露,因为作为一个未引用的块,软件无法找到它来清理它。

另一方面,内存泄漏是程序的内存使用量无限增长的任何情况。在分析软件作为服务运行时可能会失败的情况时(定义服务运行,可能一次持续数月),此定义非常有用。

因此,任何可持续保留对不需要的对象的引用的可扩展数据结构都可能导致服务软件因地址空间耗尽而有效失败。

答案 5 :(得分:0)

最简单的内存泄漏:

public static class StaticStuff
{
    public static event Action SomeStaticEvent;
}

public class Listener
{
   public Listener() {
      StaticStuff.SomeStaticEvent+=DoSomething;
   }
   void DoSomething() {}
}

永远不会收集监听器的实例。

答案 6 :(得分:-1)

如果我们将内存泄漏定义为可以用于创建对象的内存,无法使用的内存或者可以释放的内存那么

内存泄漏可能发生在:

  • WPF中需要使用弱事件的事件。这尤其可以在附加属性
  • 中进行
  • 大型物件

Large Object Heap Fragmentation

http://msdn.microsoft.com/en-us/magazine/cc534993.aspx