JVM垃圾回收-finalization机制& 安全点& 安全区域(二)

   日期:2020-09-28     浏览:148    评论:0    
核心提示:finalization& Safe Point& Safe Regionfinalization机制概述对象的3种状态销毁对象之前具体判断过程演示例子安全点与安全区域安全点(Safepoint)安全区域(Safe Region)finalization机制概述当垃圾回收时对象被销毁前, 总会先调用对象的 finalize()方法(被调用的前提是必须重写此方法, 同时未被调用过, 因为此方法只会调用一次), 常用于处理资源释放 如关闭文件, 套接字, 数据库连接等对象的3种状态

finalization& Safe Point& Safe Region

  • finalization机制概述
    • 对象的3种状态
      • 销毁对象之前具体判断过程
        • 演示例子
  • 安全点与安全区域
    • 安全点(Safepoint)
    • 安全区域(Safe Region)

finalization机制概述

  • 当垃圾回收时对象被销毁前, 总会先调用对象的 finalize()方法(被调用的前提是必须重写此方法, 同时未被调用过, 因为此方法只会调用一次), 常用于处理资源释放 如关闭文件, 套接字, 数据库连接等

对象的3种状态

  • 当一个对象与 GC Roots失去联系时, 就意味着对象已经不再使用了. 通常进入垃圾回收时就会被回收, 但是如果重写了 finalize()方法, 虚拟机不会立马回收, 而调用 finalize()方法缓一次回收(也就是给了一次复活的机会)
  1. 可触及的: 可达对象(与 GC Roots直接或间接的联系着)
  2. 可复活的: 不可达对象(失去了与 GC Roots的联系), 但有一次执行 finalize()方法缓一次回收的机会(此时可以编写复活相关逻辑)
  3. 不可触及的: 已调用过一次 finalize()方法, 且没有复活. 此时的状态就是不可触及的状态. 此时进入回收, 将肯定会被回收

销毁对象之前具体判断过程

  • 判定一个对象A是否可回收, 会经过两次标记过程:
  1. 如果对象A与 GC Roots失去联系, 则进行第一次标记
  2. 是否执行 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点:
  1. 在 finalize()时可能会导致对象复活
  2. finalize()方法的执行时间是没有保障的, 它完全由 GC线程决定, 极端情况下, 若不发生GC, 则 finalize()方法将没有执行机会
  3. 一个糟糕的 finalize()会严重影响 GC的性能(重写的情况)
  • finalize()方法区别于 C++的析构函数, 虽然比较相似, 但 Java是基于垃圾收集器的自动内存管理机制, 所以本质上不同于 C++的析构函数

安全点与安全区域

安全点(Safepoint)

  • 程序执行时并非在所有地方都能停顿下来开始 GC, 只有在特定的位置才能停顿下来进行 GC, 这些点成为安全点
  • 当 JVM要触发 GC, 偏向锁解除等操作时, 所有的用户线程都必须到达安全点

安全区域(Safe Region)

  • 安全点保证了程序执行过程中的时间隔比较近的安全点. 但是有些线是处于睡眠/阻塞/等待执行的状态(此时线程是无法响应 JVM的中断请求的), 此时就会用到安全区域
  • 安全区域是指在一段代码片段中, 对象的引用关系不会发生变化, 在这个区域中的任何位置开始 GC都是安全的
  • 当线程运行到安全区域的代码片段时, 首先会标识已经进入了安全区域, 如果这段时间内发生 GC, JVM会忽略标识为安全区域的线程
  • 当线程即将离开安全区域时, JVM会检查是否已经完成 GC, 如果完成了, 则继续运行, 否则线程必须等待直到收到可以安全离开安全区域的信号为止

如果您觉得有帮助,欢迎点赞哦 ~ 谢谢!!

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服