c# - .NET 服务器垃圾回收和对象生命周期

标签 c# .net memory-management garbage-collection

我自己和一位同事对于何时可以在 .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 了?

最佳答案

这个问题涉及的内容很多,所以我将首先解决最重要的问题。

  1. using 语句中声明的变量在 block 结束之前不会被垃圾回收,原因正是您指出的 - 保留引用以便调用 Dispose() 在隐式 finally block 中。

  2. 如果您发现自己在用 C# 编写终结器,则您可能做错了什么。如果 C# 中的终结器调用 Stream.Dispose(),您肯定做错了什么。在 .NET 本身的实现之外,我看到了数百个被滥用的终结器,以及 exactly 1 finalizer这实际上是需要的。有关详细信息,请参阅 DG Update: Dispose, Finalization, and Resource Management .

  3. ObjectDisposedException 与终结无关。此异常通常发生在代码调用对象上的 Dispose() 时(未完成),然后稍后调用对该对象执行某些操作。

    有时代码处理 Stream 时并不明显。一个让我感到惊讶的案例是使用 StreamContent作为使用 HttpClient 发送 HTTP 请求的一部分。该实现在发送请求后调用 Stream.Dispose(),因此我必须编写一个名为 DelegatingStream 的包装器 Stream 类在从 HttpWebRequestHttpClient 的转换过程中保留我们库的原始行为。

您可能会看到 ObjectDisposedException 的一种情况是,如果 getStream() 方法缓存了一个 Stream 实例并返回它以供将来多次调用.如果 Request.Dispose() 处理流,或者如果 DoStuff2(Stream) 处理流,那么下次您尝试使用流时,您将获得一个 ObjectDisposedException

关于c# - .NET 服务器垃圾回收和对象生命周期,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27316318/

相关文章:

c++ - 为类重载 new

c# - 检查 Datagrid 中 ItemTamplate 标签的值

c# - 无法连接到网络服务器 'IIS Express'

c# - 跨 AppDomain 边界代理匿名对象

c# - 解码引用可打印正确

javascript - 将对象转换为 JSON 字符串并将其置空

c# - 在 C# 中获取视频剪辑的帧率

c# - 如何从 Awesomium 网页控件复制 Html?

c# - 包裹或不包裹

C - 当函数内部分配内存时,谁负责内存分配?