[只是想进一步添加有关终结过程的内部信息]
您创建了一个对象,当该对象被垃圾回收时,应调用该对象的Finalize
方法。但终结不仅仅是这个非常简单的假设。
概念:
没有实现Finalize
方法的对象:它们的内存立即被回收,除非它们已经不再被应用程序代码所引用。
实现了Finalize
方法的对象:需要理解应用程序根
、终结队列
、Freachable队列
等概念,因为它们参与到回收过程中。
如果任何对象无法被应用程序代码访问,则被视为垃圾。
假设:类/对象A、B、D、G、H不实现Finalize
方法,而C、E、F、I、J则实现了Finalize
方法。
当一个应用程序创建一个新对象时,
new
运算符从堆中分配内存。如果对象的类型包含一个
Finalize
方法,则指向该对象的指针将被放置在finalization队列中。因此,指向对象C、E、F、I、J的指针被添加到finalization队列中。
finalization队列是由垃圾收集器控制的内部数据结构。队列中的每个条目都指向一个对象,该对象在其内存可以被回收之前应调用其
Finalize
方法。
下图显示了一个包含多个对象的堆。其中一些对象可以从应用程序根访问,而另一些则不能。当创建对象C、E、F、I和J时,.NET框架检测到这些对象具有
Finalize
方法,并将指向这些对象的指针添加到
finalization
队列中。
![enter image description here](https://istack.dev59.com/33Wsz.gif)
当进行GC(第一次收集)时,对象B、E、G、H、I和J被确定为垃圾。A、C、D和F仍然可以从上面的黄色框中的应用程序代码箭头到达。
垃圾回收器扫描终结队列,查找指向这些对象的指针。当找到指针时,将该指针从终结队列中删除并附加到可达队列(“F-reachable”,即finalizer reachable)。可达队列是另一个由垃圾回收器控制的内部数据结构。可达队列中的每个指针都标识一个已准备好调用其Finalize方法的对象。
第一次GC后,托管堆看起来类似下图所示。以下是说明:
The memory occupied by objects B, G, and H has been immediately reclaimed because these objects do not have a finalize method that needs to be called. However, the memory occupied by objects E, I, and J cannot be reclaimed because their Finalize method has not yet been called. Calling the Finalize method is done through the freachable queue. Objects A, C, D, and F are still reachable by application code depicted as arrows from the yellow box above, so they will not be collected in any case.
![enter image description here](https://istack.dev59.com/A4qf2.gif)
有一个特殊的运行时线程专门用于调用Finalize方法。当可回收队列为空(通常情况下),该线程会进入睡眠状态。但是当条目出现时,该线程会唤醒,从队列中移除每个条目,并调用每个对象的Finalize方法。垃圾收集器压缩可回收内存,特殊的运行时线程清空可回收队列,执行每个对象的Finalize方法。所以这就是你的Finalize方法最终被执行的时候。
下一次垃圾收集器被调用时(第二次GC),它会看到已经完成了清理的对象是真正的垃圾,因为应用程序的根不再指向它,而可回收队列也不再指向它(它也是空的),因此可以从堆中回收对象E、I、J的内存。请参见下面的图表,并将其与上面的图表进行比较。
![enter image description here](https://istack.dev59.com/atQbH.gif)
重要的是要明白,需要两个GC(Garbage Collection)来回收需要终结的对象所使用的内存。实际上,可能需要更多的垃圾回收操作,因为这些对象可能会晋升到较旧的代中。
注意:freachable队列被视为根,就像全局变量和静态变量一样是根。因此,如果一个对象在freachable队列中,则该对象是可达的,不是垃圾。
最后请记住,调试应用程序是一回事,垃圾回收是另一回事,并且工作方式不同。到目前为止,您无法仅通过调试应用程序来感知垃圾回收。如果您希望进一步研究内存,请从
这里开始。