代码之家  ›  专栏  ›  技术社区  ›  Kevin Meredith

垃圾收集器没有清理哪些对象?

  •  5
  • Kevin Meredith  · 技术社区  · 14 年前

    静态分析工具一直告诉我,我的C代码中有资源泄漏。

    下面是一个例子:

    StringReader reader = new StringReader(...); 
    
    // do something with reader
    
    ...
    
    } // static analysis tool cries that I've leaked **reader**
    

    我的工具正确吗?如果是,为什么?

    编辑 (回复评论)-我的静态分析工具说我有很多资源泄漏。我从这知道 forum 某些Java AWT对象需要显式释放,否则会发生泄漏。是否存在需要显式释放的C对象?

    9 回复  |  直到 13 年前
        1
  •  13
  •   Darin Dimitrov    14 年前

    是的,你的代码泄漏严重。应该是这样的:

    using (StringReader reader = new StringReader(...))
    {
    
    }
    

    实现的每个类 IDisposable 需要包裹在 using block 以确保 Dispose 始终调用方法。


    更新:

    详细说明:在.NET中有 IDisposable 定义Dispose方法的接口。实现此接口的类(如文件流、数据库连接、读卡器等)可能包含指向非托管资源的指针,确保释放这些非托管资源/句柄的唯一方法是调用Dispose方法。因此,在.NET中,要确保调用某些代码,即使引发异常,也要使用try/finally语句:

    var myRes = new MyResource(); // where MyResource implements IDisposable
    try
    {
        myRes.DoSomething(); // this might throw an exception
    }
    finally
    {
        if (myRes != null)
        {
            ((IDisposable)myRes).Dispose();
        }
    }
    

    编写C代码的人很快就意识到,每次处理一次性资源时编写这个代码都是一个pita。所以他们介绍了 using 声明:

    using (var myRes = new MyResource())
    {
        myRes.DoSomething(); // this might throw an exception
    }
    

    稍微短一点。

        2
  •  6
  •   Jon Skeet    14 年前

    你的代码不是 事实上 在这种特殊情况下泄漏任何东西,因为 StringReader 没有任何资源可以清理,就像 MemoryStream 没有。(与 内存流 可以 如果您正在异步使用它或远程处理它,最后仍然需要处理它…但在简单的情况下,这并不重要。)

    但是,最好处理任何实现 IDisposable 只是原则上的。这样可以避免泄漏(可能是暂时的) 非受管的 文件句柄等资源。

    例如,假设您将代码更改为:

    StreamReader reader = new StreamReader("file.txt");
    ...
    

    如果你不关闭或处理 reader 这里(在A finally 阻塞或通过 using statement )它将保持文件打开,直到直接持有操作系统文件句柄的任何类型被最终确定。显式地处理事物不仅可以更早地释放非托管资源,还可以从终结队列中除去可终结的对象,这意味着可以更早地对它们进行垃圾收集。

        3
  •  2
  •   Jon B    14 年前

    看起来您没有处理StringReader。你需要打电话 .Dispose() 清理非托管资源。或者更好的是,在 using 按此方式阻止:

    using (StringReader reader = new StringReader(...))
    {
        // your code
    }
    

    这将导致Dispose()自动出现在块的末尾,即使您的代码抛出异常(与使用finally块相同)。

        4
  •  2
  •   ChrisLively    14 年前

    正如其他人所说,归根结底就是StringReader没有被处理掉,所以我不会再谈这个了。

    现在的情况是静态分析工具本质上是一个愚蠢的工具。我的意思不是“哑巴”,而是“不使用”,我的意思是“哑巴”,因为它在寻找一个非常有限的标准。

    在这种情况下,它会看到一个正在实例化的对象,该对象的类实现IDisposable。然后,该工具只是查看在对象超出范围之前是否进行了相应的释放调用。这可以通过显式地说object.dispose();或通过using(var x=…)子句实现。

    根据 MS specs 类应在处理非托管资源(如文件句柄)时实现IDisposable。现在,您可能想回顾一下 MSDN post 它讨论了实现IDisposable的哪些类 对调用Dispose()。

    这给了我们两个可行的解决方案。第一个(也是我和Darin推荐的)是始终将实现IDisposable的对象包装在using子句中。这只是个很好的练习。毕竟,它不会造成任何伤害,而没有它会导致大量的内存泄漏(取决于类),而且我 not smart enough 记住哪个是哪个。

    另一种方法是配置静态分析工具(如果可能)以忽略这些警告。我真的认为这是个坏主意(tm)

        5
  •  2
  •   supercat    14 年前

    流有许多类,具有关联的读卡器对象类型。其中一些类操作外部对象的方式必须在完全放弃之前撤消,但在仍然需要时无法撤消(例如,文件读取器将打开文件;文件必须在忘记文件句柄之前关闭,但在读取之前无法关闭)。R完成了)。“Dispose”方法将处理读者在完全放弃之前必须“放回”的任何内容。因为一段代码创建一个流阅读器并将其传递给另一段代码是很常见的,而且由于第一段代码可能无法知道使用该阅读器的代码是何时完成的,所以使用该阅读器的代码有责任调用Dispose当它完成的时候。

    请注意,某些类型的读卡器实际上并不在Dispose方法中执行任何操作,但是任何接受任意类型流读卡器的代码都应该调用它。虽然您的代码需要一种不需要Dispose的流读取器类型,但可能会为它提供一个不需要Dispose的派生类。即使在不严格必要的情况下调用Dispose也可以防止出现这种情况。

        6
  •  1
  •   Cameron    14 年前

    我相信是因为你没有打电话给 Close 方法,但根据msdn:

    close的这个实现调用Dispose方法传递一个真值。

    因此,我希望当垃圾收集器到达读卡器时,它将以相同的最终结果进行处理(并且没有内存泄漏)。

    更新 :我错了,是GC的错。 自动释放IDisposable对象。您需要显式调用Close(或Dispose)。

        7
  •  0
  •   asdf    14 年前

    你已经泄密了,但总检察官最终会帮你清理。

        8
  •  0
  •   Khalid Abuhakmeh    14 年前

    StringReader可能正在访问流。如果不释放StringReader,则可能会使该流保持打开状态。流可以附加到系统上的一个文件,反过来,您可能已经锁定了它。

    尝试查找using语句,这将自动调用Dispose。

    using (sr) {
     // your code
    }
    
        9
  •  0
  •   Mike Caron    14 年前

    垃圾收集器将收集不再引用它的任何内容。在你的样本中, reader 最终会被收集(尽管没人知道何时)。

    但是,“静态分析工具”抱怨您没有手动调用 Dispose() .

    StringReader reader = ...
    ...
    reader.Dispose();
    

    在这个特定的案例中,这可能不是什么大问题。但是,在处理许多IO类(*stream、*reader等)时,最好在完成后处理它们。你可以用 using 帮助:

    using(StringReader reader = ...) {
        ...
    }  //reader is automatically disposed here