面试题答案
一键面试可达性分析判断对象是否可回收
- 数据结构:
- GC Roots:是可达性分析的起始点集合。包括虚拟机栈(栈帧中的本地变量表)中引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象、本地方法栈中JNI(即一般说的Native方法)引用的对象等。这些对象被认为是“绝对不会被回收的”。
- 分析流程:
- 从GC Roots集合中的每一个根对象开始,按照对象之间的引用关系,通过引用链进行遍历。
- 能够被遍历到的对象,即从GC Roots可达的对象,被标记为“存活”,不可达的对象则被标记为“可回收”。
- 例如,有一个对象A,它被栈帧中的本地变量表引用,那么A就是从GC Roots可达的,在可达性分析中会被标记为存活。如果对象B没有任何从GC Roots出发的引用链与之相连,那么B就是不可达的,会被标记为可回收。
- 可能出现的问题及解决方案:
- 问题:
- 对象的自我救赎:在可达性分析后,对象被判定为不可达,但对象可能覆盖了
finalize()
方法,并且在该方法中重新与GC Roots建立了连接,从而“复活”。这种情况可能导致对象本应被回收却没有被回收。 - 对象的循环引用:两个或多个对象相互引用,形成一个封闭的引用环,而环中的对象都没有与GC Roots相连。从整体上看,这些对象是不可达的,但传统的可达性分析可能会误判它们为存活对象。
- 对象的自我救赎:在可达性分析后,对象被判定为不可达,但对象可能覆盖了
- 解决方案:
- 针对对象的自我救赎:Java虚拟机对
finalize()
方法的调用是有且仅有一次。如果某个对象在第一次被判定为不可达时,通过finalize()
方法重新可达,那么在后续再次被判定为不可达时,不会再调用finalize()
方法,直接回收该对象。 - 针对对象的循环引用:在可达性分析算法中,基于引用链遍历的方式,不会因为对象之间的循环引用而误判对象存活。只要从GC Roots出发无法到达的对象,无论是否存在循环引用,都会被判定为可回收对象。例如,对象C和对象D相互引用,但如果它们都没有从GC Roots出发的引用链相连,那么在可达性分析时,C和D都会被标记为可回收。
- 针对对象的自我救赎:Java虚拟机对
- 问题: