ath*_*hos 16 .net c# garbage-collection suppressfinalize
我正在阅读"C#语言",第4版,它讨论垃圾收集如下:
"BILL WAGNER:以下规则是C#与其他托管环境之间的重要区别.
在应用程序终止之前,会调用析构函数的所有尚未被垃圾回收的对象,除非已经抑制了这种清理(例如,通过调用库方法GC.SuppressFinalize)."
所以我在这里有几个问题:
Q1.为什么.net与其他托管环境不同(我想这是暗示Java?)?任何特定的设计问题?
Q2.GC.SuppressFinalize调用的对象会发生什么?我明白这意味着GC不会调用这些对象的终结器(析构函数),如果是这样,这些对象什么时候才会被破坏,以便分配的内存位返回堆?否则会有内存泄漏?
Eri*_*ert 39
调用GC.SuppressFinalize的对象会发生什么?我明白这意味着GC不会调用这些对象的终结器(析构函数),如果是这样,这些对象什么时候才能被破坏呢?否则会有内存泄漏吗?
您对最终确定的内容存在误解.最终确定用于清理非托管内存的资源.
假设您有一个包含整数字段的引用类型的对象.该整数字段恰好是通过调用非托管代码打开文件获得的文件的句柄.
由于某些其他程序可能想要访问该文件,因此请尽快关闭该文件.但.NET运行时不知道这个整数对操作系统有任何特殊含义.它只是一个整数.
解决此问题的方法通常是将对象标记为实现IDisposable,然后在完成后立即调用对象上的"Dispose".然后,您的"Dispose"实现将关闭该文件.
请注意,这里没有什么特别之处.清理非托管资源的方法称为"Dispose",而需要处理的对象实现IDisposable只是一种约定.垃圾收集对此一无所知.
所以现在问题出现了:如果有人忘记调用Dispose怎么办?文件永远保持打开状态吗?(显然,当进程结束时文件将被关闭,但是如果进程运行了很长时间怎么办?)
要解决此问题,请使用终结器.这是如何运作的?
当一个对象即将被垃圾收集时,垃圾收集器会检查它是否有一个终结器.如果是,那么它将其放在终结器队列上,而不是垃圾收集它.在未来的某个未指定的点上,运行一个线程来检查队列并在每个对象上调用一个特殊的"Finalize"方法.之后,该对象将从终结队列中删除并标记为"嘿,我已经完成了".该对象现在再次可以进行收集,因此垃圾收集器最终运行并收集对象而不将其放在终结队列中.
显然,"终结"和"处理"经常需要做同样的事情.
但现在又出现了另一个问题.假设您处置了一个对象.现在它不需要最终确定.定稿很昂贵; 它使一个死对象活着的时间比它需要的时间长得多.因此,传统上当一个人处理一个对象时,Dispose的实现不仅会关闭非托管资源,还会将对象标记为"此对象已经完成,不再对其进行最终确定".这样它就会欺骗垃圾收集器,而不是将对象放在终结队列上.
那么让我们回答您的具体问题:
调用GC.SuppressFinalize的对象会发生什么?
当对象死亡时,垃圾收集器将简单地回收对象的内存而不将对象放在终结器队列上.
我知道这意味着GC不会调用这些对象的终结器
GC 从不调用终结器.终结器线程是唯一调用终结器的东西.
什么时候这些物体真的被毁坏了?
目前尚不清楚"被毁"是什么意思.如果你的意思是"终结者什么时候会运行?" 答案是"从不",因为你说压制定稿.如果您的意思是"何时回收托管堆中的内存?",答案是"一旦垃圾收集器将对象识别为死亡".这将比正常情况更早发生,因为终结器队列不会保持对象的活动.
Q1:我怀疑这是因为它更关心实现卓越的性能,而不是Java为简单性做出了不少牺牲.
Q2:由于终结器甚至不能保证首先被调用(即使SuppressFinalize不存在),因此当您已经处理完资源时,这应仅用于性能原因.否则你应该IDisposable用来处置资源.
.NET中不存在析构函数(在C++意义上) - 因为在某种意义上,每个对象都有一个"析构函数",称为垃圾收集器.:)
C#调用"析构函数"实际上是终结符.终结是用于处置的东西其他比对象分配内存,如文件句柄等并不是每一个对象,因此,有一个终结.内存总是由GC释放,因此您不会以这种方式泄漏内存.