JVM垃圾回收篇(对象终止机制)

Posted 编程小吉

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM垃圾回收篇(对象终止机制)相关的知识,希望对你有一定的参考价值。

对象终止机制

1.什么是对象终止机制?

  • Java语言提供了对象终止( finalization )机制来允许开发人员提供对象被销毁之前的自定义处理逻辑处理
  • 当垃圾回收器发现没有任何引用指向某个对象时,那么就会在垃圾回收中清除这个对象,在垃圾回收器回收此对象之前,会先调用这个对象的 finalize() 方法
  • 我们发现 finalize() 方法允许在子类中被重写,所以我们可以利用这个重写的方法,用于在对象被回收时进行资源的释放操作或者一些清理操作,比如:关闭文件、套接字和数据库连接等
  • 从功能上来说,finalize() 方法与 C++ 中的析构函数比较相似,但是Java采用的是基于垃圾回收器的自动内存管理机制,它们在本质上还有区别

2.对象终止的注意细节

  • finalize() 方法在一个对象生命周期中,只会被调用一次( 即使被复活过 )
  • 我们开发人员切记不要主动调用某个对象的 finalize() 方法,而是应该交给垃圾回收机制去调用
  • 因为在 finalize() 方法中,我们可以导致对象重新复活,也就是不让垃圾回收器回收,这会影响垃圾回收器本身的工作
  • 因为 finalize() 方法的执行时间是没有保障的,它完全由GC线程决定,也就是说,若不发生GC,那么 finalize() 方法将永远没有机会执行
  • 因为一个糟糕的 finalize() 方法会严重影响GC的性能,所以也不建议自己去调用

3.对象的三种生命状态

由于finalize()方法的存在,虚拟机中的对象一般会有三种可能的状态

如果从所有的根节点都无法访问到某个对象,说明对象己经不再使用了。一般来说,此对象需要被回收。但事实上,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段。因为一个无法触及的对象有可能在某一个条件下“复活”自己,如果这样,那么对它的回收就是不合理的

  • 可触及的状态:从GC Roots根节点开始,可以到达这个对象
  • 可复活的状态:对象的所有引用都已经被释放,但是对象有可能在 finalize() 方法中复活
  • 不可触及的状态:对象的 finalize() 被调用后发现也没有被复活,那么就会进入不可触及状态,不可触及的对象不可能再被复活,因为 finalize() 只会被调用一次

4.对象的回收判定过程

判定一个对象A是否可回收,至少要经历两次标记过程

  • 如果对象A到GC Roots没有引用链,则进行第一次标记
  • 如果对象A没有重写 finalize() 方法,或者 finalize() 方法已经被虚拟机调用过了,则虚拟机视为“没有必要执行”,此时对象A被判定为不可触及的状态
  • 如果对象A重写了 finalize() 方法,且还未执行过,那么对象A会被插入到F-Queue的队列中,然后由虚拟机自动创建的、低优先级的Finalizer线程触发其 finalize() 方法执行
  • 对象A会被插入到F-Queue的队列中后, GC会对这个队列中的所有对象进行第二次标记
  • 如果对象A在 finalize() 方法中也没有与引用链上的任何一个对象建立联系,那么在第二次标记时,对象A就会被判定为不可触及的状态
  • 如果对象A在 finalize() 方法中与引用链上的任何一个对象建立了联系,那么在第二次标记时,对象A就会被判定为可复活的状态。 但是当对象再次出现没有引用存在的情况时, finalize() 方法也不会被再次调用,直接被判定为不可触及的状态

5.对象的回收程序演示

public class CanReliveObj 
    public static CanReliveObj obj;

	// 重写finalize方法(第二次标记)
    @Override
    protected void finalize() throws Throwable 
        super.finalize();
        System.out.println("调用当前类重写的finalize()方法");
        obj = this;
    

    public static void main(String[] args) 
        try 
            obj = new CanReliveObj();

            obj = null;
            System.gc(); // 第一次标记
            System.out.println("第1次 gc");
            Thread.sleep(2000);
            if (obj == null) 
                System.out.println("obj is dead");
             else 
                System.out.println("obj is still alive");
            
			
            obj = null;
            System.gc();
            System.out.println("第2次 gc");
            Thread.sleep(2000);
            if (obj == null) 
                System.out.println("obj is dead");
             else 
                System.out.println("obj is still alive");
            
         catch (InterruptedException e) 
            e.printStackTrace();
        
    

当把上面重写的finalize方法取消时

当把上面重写的finalize方法加上时

6.什么是GC Roots?

上面提到的GC Roots是什么,它是垃圾回收器在垃圾回收时进行判断的根搜索对象集合,那么如何利用工具查看代码中的GC Roots呢?

编写测试代码

public class GCRootsTest 
    public static void main(String[] args) 
        List<Object> numList = new ArrayList<>();
        Date birth = new Date();

        for (int i = 0; i < 100; i++) 
            numList.add(String.valueOf(i));
            try 
                Thread.sleep(10);
             catch (InterruptedException e) 
                e.printStackTrace();
            
        

        System.out.println("数据添加完毕...");
        new Scanner(System.in).next();
        numList = null;
        birth = null;

        System.out.println("对象已被置空...");
    

获取堆内存的Dump文件

  • 命令行方式

    # 查看当前进程
    jps
    # 生成dump文件
    jmap -dump:format=b,live,file=test1.bin 进程ID
    

  • 工具导出方式

    使用 JVisualVM 工具


使用工具分析dump文件

  • MAT工具

    MAT是 Memory Analyzer 的简称, 是由Eclipse开发的,它是一 款功能强大的Java堆内存分析器,用于查找内存泄漏以及查看内存消耗情况,下载地址: http://www.eclipse/org/mat/

  • JProfiler工具

以上是关于JVM垃圾回收篇(对象终止机制)的主要内容,如果未能解决你的问题,请参考以下文章

jvm_垃圾回收&对象的finalization机制详解

JVM垃圾回收篇(基本概述)

Java垃圾回收机制

大数据基础篇----jvm的知识点归纳-5个区和垃圾回收机制

5、垃圾回收机制

Java中的内存泄露 和 JVM GC(垃圾回收机制)