五分钟了解JVM-GC垃圾收集器(对象是否死亡?)| 8月更文挑战

412 阅读3分钟

这是我参与8月更文挑战的第2天

一、为什么要进行垃圾收集?

随着前面两章内容的介绍,我们了解了Java内存运行时区域的各个部分。其中的程序计数器、虚拟机栈、本地方法栈,随线程而生,也随线程而灭,因此这几个区域的内存分配和回收都具有确定性。

另外两个区域(方法区、堆)则有显著的不确定性,只有在程序运行期间,我们才能知道程序会创建那些对象、多少对象,这部分的内存回收是动态的。而垃圾收集器关注的正是这部分内存,而进行垃圾收集唯一的目的就是释放该区域的内存空间。

二、如何判断对象已死

在堆内存中存放着Java世界中几乎所有的实例,垃圾收集器在堆进行回收前,第一件事情就是确认哪些对象还是“存活”,哪些对象已“死去”。

这里列举两种算法:

  • 引用计数算法 这很好理解,在对象中添加一个引用计数器,没有一个地方引用他,计数器加一,引用失效时,计算器减一;任何时刻计数器为零的对象就是不能在被使用的。

存在问题: 如果A引用B,B引用A,而且都是无效引用(这两个对象除了在互相引用,其他地方并未有声明代码),这样导致了虽然存在相互引用,但其实是无效引用。

  • 可达性分析算法: 通过一系列的GC Roots 的根作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”,如果某个对象到GC Roots间没有任何引用路径,则判定为可回收对象。 固定作为GC Roots对象:

1)、虚拟机栈中引用的对象,当前运行方法所使用到的参数、局部变量、临时变量

2)、方法区中类静态属性引用的变量

3)、在方法区中常量引用的对象

4)、在本地方法栈中引用的对象

5)、虚拟机内部调用的对象

6)、所有被同步锁持有的对象

7)、反应Java虚拟机内部情况的JMXBean

三、对象自救

即使在可达性分析算法中判定为不可达对象,也不是非死不可。如果对象在进行可达性分析发现没有与GC Roots相连接的引用链,那么它将会第一次被标记,随后再进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。

如何自我救赎:

1.对象覆写了finalize()方法(这样在被判死后才会调用此方法,才有机会做最后的救赎);

2.在finalize()方法中重新引用到"GC  Roots"链上(如把当前对象的引用this赋值给某对象的类变量/成员变量,重新建立可达的引用)