上一篇:008-JVM-清除阶段算法之三:标记-压缩算法Mark-Compact
https://blog.csdn.net/fsjwin/article/details/111342195
标记为不可达的对象一定会被回收吗
?
这是一个好问题,一般这么问的答案肯定是否定的!!!
Object.java 中的方法:
protected void finalize() throws Throwable { }
1. finalization机制存在的必要性
finalization机制简单说就是免死金牌,或者在沙头前的一道圣旨。
- Java 语言提供了对象终止(finalization) 机制来允许开发人员提供对象被销毁之前的自定义处理逻辑。
- 当垃圾回收器发现没有引用指向一个对象, 即: 垃圾回收此对象之前, 总会先调用这个对象的finalize()方法。
- finalize()方法允许在子类中被重写, 用于在对象被回收时进行资源释放(当然也可以随便做些事情如,不让他回收,让他重新复活)。通常在这个方法中进行一些资源释放和清理的工作, 比如关闭文件、套接字和数据库连接等。
2. 永远不要主动调用某个对象的finalize()方法
- 永远不要主动调用某个对象的finalize()方法, 应该交给垃圾回收机制调用。理由包括下面三点:
- 在finalize() 时可能会导致对象
复活
。 - finalize() 方法的执行时间是没有保障的, 它完全由GC 线程决定, 极端情况下,若不发生GC ,
则finalize() 方法将永远没有执行机会
。
一个糟糕的 finalize() 会严重影响GC 的性能
。
3. 生存还是死亡
- 如果从所有的根节点都无法访问到某个对象, 说明对象己经不再使用了。一般来说,此对象需要被回收。但事实上, 也并非是“ 非死不可” 的, 这时候它们暂时处于“ 缓刑” 阶段。一个无法触及的对象有可能在某一个条件下“ 复活” 自己, 如果这样, 那么对它的回收就是不合理的, 为此, 定义虚拟机中的对象可能的三种状态。如下:
可触及的
: 从根节点(GCRoot)开始, 可以到达这个对象。可复活的
: 对象的所有引用都被释放, 但是对象有可能在 中复活。不可触及的
: 对象的 finalize() 被调用, 并且没有复活, 那么就会进入不可触及状态。不可触及的对象不可能被复活, 因为 finalize() 只会被调用一次。
以上3 种状态中, 是由于 finalize() 方法的存在, 进行的区分。只有在对象不可触及时
才可以被回收。
4. 专门的线程收集垃圾
有一个优先级比较低的线程,专门调用finalize()方法
5. 具体过程
- 判定一个对象。obj 是否可回收, 至少要经历
两次标记
过程:
- 如果对象obj 到GC Roots 没有引用链, 则进行
第一次标记
。 - 进行筛选, 判断此对象是
否有必要
执行finalize()方法。
(1)如果对象obj没有重写 finalize() 方法, 或 finalize() 方法己经被虚拟机调用过,则虚拟机视为“ 没有必要执行” 。obj被判定为不可触及的
。
(2) 如果对象obj重写了 finalize() 方法, 且还未执行过
, 那么obj会被插入到F-Queue
队列中, 由一个虚拟机自动创建的、低优先级的 finalizer线程触发其 finalize() 方法执行。
(3) finalize() 方法是对象逃脱死亡的最后机会
, 稍后GC 会对F-Queue 队列中的对象进行第二次标记: 如果obj在 finalize() 方法中与引用链上的任何一个对象建立了联系,那么在第二次标记时,obj会被移出“ 即将回收 F-Queue” 集合。 - 之后, 对象会再次出现
没有引用存在的情况
。在这个情况下 finalize() 方法不会被再次调用, 对象会直接变成不可触及
的状态, 也就是说,一个对象的 finalize() 方法只会被调用一次
。
6. 复活演示
关键点:finalize()方法中和gc roots中的对象发生关系,就可免一死!
下一篇:010-JVM-Java从编译到执行的流程 https://blog.csdn.net/fsjwin/article/details/111404949