【问题标题】:Does the "using" keyword mean the object is disposed and GC'ed?“using”关键字是否意味着对象已被释放并被 GC 处理?
【发布时间】:2014-04-22 07:29:02
【问题描述】:

我今天和我的同事聊了起来,她说她刚刚知道了使用 using 声明背后的原因。

 //Using keyword is used to clean up resources that require disposal (IDisposable interface). 

 using (StreamReader reader = new StreamReader(@"C:\test.txt")) 
 { 
 string line = reader.ReadLine(); 
 } 

我指出该对象被标记为“可以处置”,但实际上并没有被处置和垃圾回收,除非 GC 决定这样做。

她回应说,一旦 using 语句结束,对象将被自动释放,因为 using 语句被转换为 try-catch-finally 块。因此,对象必须在 using 语句的最后被释放。

我对此感到困惑,因为我知道使用 using 语句并不能保证对象会被 GC 收集。所发生的只是调用Dispose() 方法。 GC 决定何时对它进行 GC。但是当她要证据时,我找不到任何证据。

有谁知道这是如何工作的,以及如何证明它?

【问题讨论】:

标签: c# .net garbage-collection dispose


【解决方案1】:

你说的是两种截然不同的东西。

对象将在using-block 结束后立即释放。这并没有说明它何时被垃圾收集。唯一一次释放堆内存是在垃圾回收发生时——这只会在内存压力下发生(除非你明确使用GC.Collect)。

释放一个对象仅仅意味着调用它的Dispose 方法。这通常意味着释放稀缺资源或本地资源(实际上,所有稀缺资源都是本地资源——套接字、文件……)。现在,在您的情况下,一次性对象的生命周期在范围内受到限制是很方便的,因此理论上可以在 using-block 结束后立即收集它 - 但是,这在实践中并没有真正发生,因为 . NET 运行时试图避免收集——它们很昂贵。因此,在您超过内存分配阈值之前,不会发生任何收集,即使堆上有一个死对象。

那么Dispose 的意义何在?与托管内存无关。您并不真正关心托管内存,并且您不应该期望 Dispose 实际上会被调用 - 它不一定是。 必须由运行时调用的唯一东西是终结器,您只能使用它来处理本机资源 - 事实上,不能保证您引用的对象仍然在终结器运行时存在 - 托管内存可能已经被回收。这就是您从不在终结器中处理托管资源的原因。

所以是的,她完全正确。关键是IDisposable 与垃圾收集器无关。 Disposed 并不意味着垃圾收集。

【讨论】:

  • 另外,您可以在这篇文章中了解更多信息:stackoverflow.com/questions/11927827/…
  • 这个答案大部分是正确的,但是过于强调一次性模式与垃圾收集无关。这仅适用于不可终结的对象。任何带有终结器的对象都会被 GC 区别对待,如果对象没有被释放(包括 GC.SuppressFinalize),它可能会在其内存被回收之前停留更长时间。
  • @DominicCronin 没错,但这只是捷径——最好尽快释放原生资源。事实上,垃圾收集器不知道你的非托管内存;终结器正是因为这个原因 - 它允许您安全地处理非托管内存,方法是将必要的终结代码集中在将在某个时候调用的东西中 - 作为优化,可以抑制 - 这通常在处置方法。这就是为什么我说 GC 与 IDisposable 无关 - GC 不在乎,你必须在乎。
  • 在某些情况下,运行时没有有调用你的终结器,所以将它用于任何具有语义重要性的东西不是一个好主意。显然,突然的进程终止不算在内,但是一个行为不端的终结器可能会导致运行时在它试图关闭而不调用其他任何东西时放弃。但是,在运行时,您确实可以保证如果确实存在集合,将调用终结器。
  • @Luaan:当然,是的,你不需要担心一大堆问题,这就是我指定语义重要性的原因。我真的不考虑释放文件句柄以具有任何有用的语义,因为无论它发生与否都不应该改变你的行为。
【解决方案2】:

她在钱上是对的 using 声明只是 try/finally{obj.Dispose();} 的语法糖。 using 语句确保对象将被释放。(将调用 Dispose 方法)但它与垃圾回收无关。

看看这个Understanding-the-using-statement

简短回答:所以现在我们知道 using 语句只是调用 Dispose 并且除此之外什么都不做,请记住 Dispose 方法与任何其他方法没有什么特别之处。这只是一种方法,仅此而已。所以它与垃圾收集没有任何关系。有趣的是,“垃圾收集器”甚至不知道 Dispose 方法或 IDisposable。

希望对你有帮助

【讨论】:

  • 所以 object 将是 null 但仍保留在 heap 上,除非 GC GC 是它。正确吗?
  • 对象不会被转到null,除非您将其明确设置为null。如果您在某处有对该对象的实时引用,则该对象将存在于内存中,尽管该对象已被释放。
  • @Luaan 你确实是对的,但那是另一回事。这深入到了 jit 优化和 GC 的深度。为了保持简单而不是迂腐,我这样说:)
  • +1 "Garbage Collector" doesn't even know about Dispose method or IDisposable
  • @PhilSandler 是的,我措辞错误,它是try...finally{Dispose()} 的语法糖。这有意义吗?
猜你喜欢
  • 2016-05-16
  • 1970-01-01
  • 2013-02-05
  • 1970-01-01
  • 2011-11-16
  • 1970-01-01
  • 2013-08-20
  • 2017-08-01
  • 1970-01-01
相关资源
最近更新 更多