[只是想进一步添加有关Finalization的内部信息]
因此,创建一个对象,并在收集该对象Finalize
时应调用该对象的方法。但是,除了这个非常简单的假设之外,最终确定还有更多。
简短概念::
对象未实现Finalize
方法,那里的内存会立即回收,除非,当然,
应用程序代码无法再访问它们
对象实施Finalize
方法,概念/实施Application Roots
,Finalization Queue
,Freacheable Queue
谈到他们可以被回收之前。
如果应用程序代码无法访问任何对象,则将其视为垃圾
假定::类/对象A,B,D,G,H不实现Finalize
方法,而C,E,F,I,J实现Finalize
方法。
当应用程序创建新对象时,新运算符将从堆中分配内存。如果对象的类型包含Finalize
方法,则将指向该对象的指针放在终结队列上。
因此,指向对象C,E,F,I,J的指针被添加到完成队列。
该结束队列是由垃圾收集器控制的内部数据结构。队列中的每个条目都指向一个对象,Finalize
在回收该对象的内存之前,应先调用其方法。下图显示了包含多个对象的堆。这些对象中的一些可以通过到达应用程序的根目录,而有些则不能。创建对象C,E,F,I和J后,.Net框架将检测到这些对象具有Finalize
方法,并将指向这些对象的指针添加到完成队列。
当发生GC(第一个集合)时,对象B,E,G,H,I和J被确定为垃圾。因为仍然可以通过上面黄色方框中的箭头所示的应用代码来访问A,C,D,F。
垃圾收集器扫描完成队列,以查找指向这些对象的指针。当找到一个指针时,该指针将从终结队列中删除,并附加到可访问队列(“ F可访问”)中。
的 freachable队列是由垃圾收集器控制的另一内部数据结构。易碎队列中的每个指针标识一个准备好拥有其对象的对象。Finalize
调用方法。
在收集(第一个收集)之后,托管堆看起来类似于下图。以下是给出的解释:
1.)对象B,G和H占用的内存已被立即回收,因为这些对象没有需要调用的finalize方法。
2.) 但是,对象E,I和J占用的内存无法回收,因为它们的内存Finalize
方法尚未被调用。
调用Finalize方法是通过易到达队列完成的。
3.) 仍然可以通过上方黄色框内的箭头所示的应用代码来访问A,C,D,F,因此在任何情况下均不会收集它们
有一个专用的运行时线程专用于调用Finalize方法。当可访问队列为空时(通常是这种情况),该线程进入睡眠状态。但是,当出现条目时,该线程将唤醒,从队列中删除每个条目,并调用每个对象的Finalize方法。垃圾收集器压缩可回收内存,特殊的运行时线程清空易碎队列,执行每个对象的Finalize
方法。所以这终于是当您的Finalize方法被执行时
下次调用垃圾收集器(第二个收集)时,它会看到最终对象是真正的垃圾,因为应用程序的根不指向该对象,并且 易碎队列不再指向该对象(它也是EMPTY),因此只需从堆中回收对象(E,I,J)的内存即可。请参见下图,并将其与上图进行比较
这里要了解的重要一点是,需要两个GC来回收由 需要终结处理对象。实际上,甚至需要两个以上的集合,因为这些对象可能会升级为较老的一代
注:: 在 freachable队列被认为是根就像全局和静态变量是根。因此,如果对象在可访问队列中,则该对象可访问且不是垃圾。
最后,请记住,调试应用程序是一回事,垃圾收集是另一回事,并且工作方式有所不同。到目前为止,您还不能仅通过调试应用程序来感受垃圾回收,如果您希望调查“内存从这里开始”的话,那么进一步。