C# 垃圾收集器不清理哪些对象?

C# 垃圾收集器不清理哪些对象?,c#,garbage-collection,C#,Garbage Collection,一个静态分析工具不断告诉我,我的C#代码中存在资源泄漏 下面是一个例子: StringReader reader = new StringReader(...); // do something with reader ... } // static analysis tool cries that I've leaked **reader** 我的工具正确吗?若然,原因为何 编辑(回复评论)-我的静态分析工具说我有一大堆资源泄漏。我从中了解到,某些JavaAWT对象需要显式释放,否则

一个静态分析工具不断告诉我,我的C#代码中存在资源泄漏

下面是一个例子:

StringReader reader = new StringReader(...); 

// do something with reader

...

} // static analysis tool cries that I've leaked **reader**
我的工具正确吗?若然,原因为何


编辑(回复评论)-我的静态分析工具说我有一大堆资源泄漏。我从中了解到,某些JavaAWT对象需要显式释放,否则会发生泄漏。是否有需要显式释放的C#对象?

您已经泄漏,但“最终”GC将为您清理。

我相信这是因为您在完成
关闭
方法后没有调用它,尽管根据MSDN:

Close的这个实现调用Dispose方法,并传递一个真值

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


更新:我错了,GC不会自动处理IDisposable对象。您需要显式调用Close(或Dispose)。

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

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

}
实现
IDisposable
的每个类都需要包装在一个包中,以确保始终调用
Dispose
方法


更新:

详细说明:在.NET中有一个定义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();
    }
}
using (var myRes = new MyResource())
{
    myRes.DoSomething(); // this might throw an exception
}
编写C#代码的人很快就意识到,每次处理一次性资源时编写C#代码都是一个难题。因此,他们使用
语句引入了

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

有点短。

stringreader可能正在访问流。通过不处理stringreader,您可能会使该流保持打开状态。流可以附加到系统上的一个文件,而您可能已经锁定了它

尝试查看using语句,它将自动为您调用dispose

using (sr) {
 // your code
}

看起来您没有处理StringReader。您需要调用
.Dispose()
来清理非托管资源。或者更好的方法是,在
中使用
块:

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

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

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

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

在这种情况下,这可能没什么大不了的。然而,当处理许多IO类(*流、*读卡器等)时,最好在完成后处理它们。您可以使用
使用
来帮助:

using(StringReader reader = ...) {
    ...
}  //reader is automatically disposed here
在这种情况下,您的代码实际上没有泄漏任何内容,因为
StringReader
实际上没有任何资源来清理,就像
MemoryStream
没有一样。(使用
MemoryStream
时,如果异步使用或远程处理它,您仍然可能需要处理它……但在简单的情况下,这并不重要。)

然而,原则上处理任何实现了
IDisposable
的东西是个好主意。这将避免泄漏(可能是暂时的)非托管资源,如文件句柄

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

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

如果您不在此处(在
finally
块中或通过a)关闭或处置
读卡器
,它将保持文件打开,直到直接保持OS文件句柄的任何类型完成。显式处理东西不仅可以更早地释放非托管资源,还可以将可终结对象从终结队列中移除,这意味着可以更早地对它们进行垃圾收集。

正如其他人所说,这归结为没有处理StringReader,所以我不想再提这一点

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

在本例中,它看到一个对象被实例化,其类实现了IDisposable。然后,该工具只是查看在对象超出范围之前是否进行了相应的dispose调用。这可以通过明确说出object.Dispose()来实现;或者通过using(var x=…){}子句

根据,类在处理非托管资源(如文件句柄)时应实现IDisposable。现在,您可能想回顾一下这篇文章,它讨论了哪些类实现了IDisposable,而您不必调用dispose()

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


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

有很多类的流,以及相关类型的读卡器对象。其中一些类操作外部对象的方式必须在完全放弃它们之前撤消,但在仍然需要它们时不能撤消(例如,文件读取器将打开文件;在忘记文件句柄之前必须关闭文件,但不能关闭)