章
目
录
Java面试题:谈谈Java GC的可达性分析
得分点:
- GC Roots
- 引用链
标准回答:
内存管理子系统通常使用可达性分析算法来判定对象是否存活。这个算法的核心思想是从一组称为“GC Roots”的根对象开始,通过引用关系向下搜索对象,搜索过程中所经过的路径被称为“引用链”。如果某个对象到GC Roots之间没有任何引用链,或者用图论的术语来说,无法从GC Roots到达该对象,那么这个对象就被认为是不可达的,即不可能再被使用,可以被回收。
在Java技术体系中,可以作为GC Roots的对象包括以下几种:
- 在虚拟机栈中引用的对象,例如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。
- 在方法区中类静态属性引用的对象,例如Java类的引用类型静态变量。
- 在方法区中常量引用的对象,例如字符串常量池中的引用。
- 在本地方法栈中引用的对象。
- JVM内部的引用,如基本数据类型对应的Class对象、常驻的异常对象以及系统类加载器。
- 所有被同步锁持有的对象。
- 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。
加分回答:
要真正宣告一个对象死亡,通常需要经历两次标记过程:
第一次标记:
在可达性分析后,如果发现对象没有与GC Roots相连接的引用链,对象将被第一次标记。然后进行一次筛选,检查对象是否需要执行finalize()方法。如果对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,那么虚拟机将视为对象“没有必要执行finalize()”。反之,将对象放入名为F-Queue的队列中,并稍后由虚拟机的Finalizer线程执行其finalize()方法。
第二次标记:
稍后,收集器将对F-Queue中的对象进行第二次小规模的标记。如果对象在finalize()方法中成功拯救自己,只需重新与引用链上的任何一个对象建立关联,例如将自己(this)赋值给某个类变量或对象的成员变量,那么在第二次标记时,对象将被移出“即将回收”的集合。如果对象无法逃脱,它将被回收。
finalize()方法是对象逃脱死亡命运的最后机会。需要注意的是,每个对象的finalize()方法只会被系统自动调用一次。此外,finalize()方法的运行代价高昂,不确定性较大,无法保证各个对象的调用顺序。因此,finalize()方法已被官方明确声明为不推荐使用的语法。