finalization& Safe Point& Safe Region
- finalization机制概述
-
- 对象的3种状态
-
- 销毁对象之前具体判断过程
-
- 演示例子
- 安全点与安全区域
-
- 安全点(Safepoint)
- 安全区域(Safe Region)
finalization机制概述
- 当垃圾回收时对象被销毁前, 总会先调用对象的 finalize()方法(
被调用的前提是必须重写此方法, 同时未被调用过, 因为此方法只会调用一次
), 常用于处理资源释放 如关闭文件, 套接字, 数据库连接等
对象的3种状态
- 当一个对象与 GC Roots失去联系时, 就意味着对象已经不再使用了. 通常进入垃圾回收时就会被回收, 但是如果重写了 finalize()方法, 虚拟机不会立马回收, 而调用 finalize()方法缓一次回收(也就是给了一次复活的机会)
- 可触及的: 可达对象(与 GC Roots直接或间接的联系着)
- 可复活的: 不可达对象(失去了与 GC Roots的联系), 但有一次执行 finalize()方法缓一次回收的机会(此时可以编写复活相关逻辑)
- 不可触及的: 已调用过一次 finalize()方法, 且没有复活. 此时的状态就是不可触及的状态. 此时进入回收, 将肯定会被回收
销毁对象之前具体判断过程
- 判定一个对象A是否可回收, 会经过两次标记过程:
- 如果对象A与 GC Roots失去联系, 则进行第一次标记
- 是否执行 finalize()方法的判断过程:
(1) 对象A重写了 finalize()方法(如没有重写意味着没有复活的过程), 同时 finalize()方法已调用过一次, 则判定对象A为不可触及的状态
(2) 对象A重写了 finalize()方法, 且还未执行过, 那么对象A会被插入到一种 F-Queue队列(引用队列)中, 由一个虚拟机自动创建的, 低优先级的 finalizer线程触发其 finalize()方法执行
(3) - 稍后(finalizer线程的触发后), 将会对 F-Queue队列中的对象进行第二次标记. 此时如果对象A在 finalize()中复活了(也就是重新建立了, 与 GC Roots的联系), 那么在此次标记时, 对象A会从’即将回收’的集合中移出. 之后, 如果对象A再次出现与 GC Roots失去联系的情况, 会直接成为不可触及的状态, 因为 finalize()方法只会被调用一次
演示例子
public class FinalizeTestApp {
public static FinalizeTestApp objA;
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("invoking `finalize` by GC");
objA = this;
}
public static void main(String[] args) {
try {
objA = new FinalizeTestApp();
objA = null;
System.gc();
System.out.println("GC 1");
Thread.sleep(2000);
if (objA == null) {
System.out.println("obj is dead");
} else {
System.out.println("obj is still alive");
}
objA = null;
System.gc();
System.out.println("GC 2");
Thread.sleep(2000);
if (objA == null) {
System.out.println("obj is dead");
} else {
System.out.println("obj is still alive");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
输出:
> GC 1
> invoking `finalize` by GC
> obj is still alive
> GC 2
> obj is dead
- 注: 永远不要主动调用对象的 finalize()方法, 应由垃圾回收机制自动调用, 理由3点:
- 在 finalize()时可能会导致对象复活
- finalize()方法的执行时间是没有保障的, 它完全由 GC线程决定, 极端情况下, 若不发生GC, 则 finalize()方法将没有执行机会
- 一个糟糕的 finalize()会严重影响 GC的性能(重写的情况)
- finalize()方法区别于 C++的析构函数, 虽然比较相似, 但 Java是基于垃圾收集器的自动内存管理机制, 所以本质上不同于 C++的析构函数
安全点与安全区域
安全点(Safepoint)
- 程序执行时并非在所有地方都能停顿下来开始 GC, 只有在特定的位置才能停顿下来进行 GC, 这些点成为安全点
- 当 JVM要触发 GC, 偏向锁解除等操作时, 所有的用户线程都必须到达安全点
安全区域(Safe Region)
- 安全点保证了程序执行过程中的时间隔比较近的安全点. 但是有些线是处于睡眠/阻塞/等待执行的状态(此时线程是无法响应 JVM的中断请求的), 此时就会用到安全区域
- 安全区域是指在一段代码片段中, 对象的引用关系不会发生变化, 在这个区域中的任何位置开始 GC都是安全的
- 当线程运行到安全区域的代码片段时, 首先会标识已经进入了安全区域, 如果这段时间内发生 GC, JVM会忽略标识为安全区域的线程
- 当线程即将离开安全区域时, JVM会检查是否已经完成 GC, 如果完成了, 则继续运行, 否则线程必须等待直到收到可以安全离开安全区域的信号为止
如果您觉得有帮助,欢迎点赞哦 ~ 谢谢!!