我自己和一位同事对于何时可以在 .NET 中对对象进行垃圾回收存在不同意见。采取以下代码:
Stream stream=getStream();
using(var request=new Request(stream))
{
Stream copy=request.Stream;
// From here on can "request" be garbage collected?
DoStuff1();
DoStuff2(copy);
}
我的同事声称,当使用服务器垃圾收集器运行发布版本时,在调用 request.Stream
之后,对 request
对象进行垃圾收集是有效的>。他断言这只会发生在服务器垃圾收集器上,而不会发生在工作站垃圾收集器上。
这是因为 Request
类有一个终结器正在关闭给请求的 Stream
。结果,当 DoStuff2
开始使用流时,它得到了一个“对象处置”异常。由于终结器只能由垃圾收集器运行,我的同事说垃圾收集必须在 finally block 结束之前发生,但在最后一次使用 request
但是,我相信,因为上面的代码只是类似这样的东西的简写:
Stream stream=getStream();
Request request=null;
try
{
Stream copy=request.Stream;
// From here on can "request" be garbage collected?
DoStuff1();
DoStuff2(copy);
}
finally
{
if(request!=null)
request.Dispose();
}
然后 request
在调用 request.Stream
之后不能被垃圾收集,因为它仍然可以从 finally
block 访问。
此外,如果垃圾收集器有可能收集对象,那么 finally
block 可能会表现出未定义的行为,因为 Dispose
将在 GC 上调用对象,这没有意义。同样,不可能优化掉 finally
block ,因为在任何垃圾收集之前,可能会在 try
/using
block 中抛出异常已经发生,这将需要执行 finally
block 。
忽略在终结器中关闭流的问题,垃圾收集器是否有可能在 finally
block 结束之前收集对象,并有效地优化掉终于
block 了?
最佳答案
这个问题涉及的内容很多,所以我将首先解决最重要的问题。
using
语句中声明的变量在 block 结束之前不会被垃圾回收,原因正是您指出的 - 保留引用以便调用Dispose()
在隐式finally
block 中。如果您发现自己在用 C# 编写终结器,则您可能做错了什么。如果 C# 中的终结器调用
Stream.Dispose()
,您肯定做错了什么。在 .NET 本身的实现之外,我看到了数百个被滥用的终结器,以及 exactly 1 finalizer这实际上是需要的。有关详细信息,请参阅 DG Update: Dispose, Finalization, and Resource Management .ObjectDisposedException
与终结无关。此异常通常发生在代码调用对象上的Dispose()
时(未完成),然后稍后调用对该对象执行某些操作。有时代码处理
Stream
时并不明显。一个让我感到惊讶的案例是使用StreamContent
作为使用HttpClient
发送 HTTP 请求的一部分。该实现在发送请求后调用Stream.Dispose()
,因此我必须编写一个名为DelegatingStream
的包装器Stream
类在从HttpWebRequest
到HttpClient
的转换过程中保留我们库的原始行为。
您可能会看到 ObjectDisposedException
的一种情况是,如果 getStream()
方法缓存了一个 Stream
实例并返回它以供将来多次调用.如果 Request.Dispose()
处理流,或者如果 DoStuff2(Stream)
处理流,那么下次您尝试使用流时,您将获得一个 ObjectDisposedException
。
关于c# - .NET 服务器垃圾回收和对象生命周期,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27316318/