对象的生存与死亡

时间:2022-12-27 16:04:58

垃圾收集器在对堆中的对象进行回收时,需要先判断对象是否还存活,如果确认该对象已经死亡,才开始回收该对象占用的内存。
判断对象是否死亡的方法有如下几种:

引用计数算法

算法描述:给对象中添加一个引用计数器,每当有一个地方引用他时,该计数器就+1,引用失效时,计数器-1,任何时候计数器为0时表示没有该对象不可能再被使用,这种时候就可以确认这个对象已经死亡,可以被回收了。

这个算法实现起来比较简单,效率也高,但是主流java虚拟机中并没有使用该方法,因为不能结局循环引用的问题;如下代码所示:

public class ReferenceCountingGC {

public Object instance = null;

private byte[] bigSize = new byte[2*1024*1024];
public static void main(String[] args) {
ReferenceCountingGC objA = new ReferenceCountingGC();
ReferenceCountingGC objB = new ReferenceCountingGC();

objA.instance = objB;
objB.instance = objA;

objA = null;
objB = null;

System.gc();
}

}

假设objA引用的在堆中的对象为A,objB引用的在堆中的对象为B,
对象A的引用有两个,分别是objA和objB.instance
对象B的引用有两个,分别是objB和objA.instance
当将objA和objB分别设为null之后,对象A和对象B的引用就分别只剩下一个了。
也就是计数器不为0,但是这两个对象在程序中已经不可能被用到了,应该被回收到,由于计数器不能为0,导致不能回收

可达性分析算法

算法描述:通过一系列的GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称之为引用链,当一个对象到GC Roots没有任何引用链相连时,证明此对象是不可用的

在java语言中,可作为GC Roots的对象包括下面几种

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法去中常量引用的对象
  • 本地方法栈中JNI(即一般说的Native方法)引用的对象

主流的java虚拟机都是使用可达性分析算法来进行判断的

引用

现阶段引用分为4中,如下:

  • 强引用(Strong Referenct):类似Object obj = new Object()这类的引用,只要强引用还在,垃圾收集器就不会回收掉被引用的对象
  • 软引用(Soft Reference):还有用但并非必需的对象,在将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常
  • 弱引用(Weak Reference):用来描述非必需对象的,强度比软引用弱一些,弱引用的对象只能存活到下一次垃圾收集之前。垃圾收集器工作时,无论内存是否足够,都会回收掉这些对象
  • 虚引用(Phantom Reference):幽灵引用或幻影引用,最弱的一种引用关系。不会对其引用的对象生存时间构成影响,也无法通过虚引用引用到一个对象,虚引用的目的是能在这个对象被收集器回收时收到一个系统通知,

finalize()

当对象被可达性分析算法计算出为不可达的对象时,对象并不是马上被回收,会先进行一次标记,判断该对象是否需要执行finalize()方法,如果finalize()方法被执行过或者对象没有覆盖finalize()方法,那么不需要执行该方法,反之,则需要执行该方法。如果需要执行finalize()方法的话,该对象将被放入到一个F-Queue的队列之中,稍后会由一个低优先级的线程去处罚它。
如果在finilize()方法中重新与引用链上的任何一个对象进行关联,那么这个方法就不会被回收。但是不建议这么做。

回收方法区

方法区又称之为永久代(Hotspot),主要回收该区域的两部分内容:废弃常量和无用的类

  • 废弃常量:如一个字符串“abc”当前系统中没有任何一个String对象是”abc”的,此时这个常量就可以被回收掉
  • 无用的类:判定条件比较苛刻,需要满足如下3个条件:
    • 类的所有实例被回收
    • 该类对象的Class对象没有在任何地方被引用,无法在任何对象通过反射访问该类的方法
    • 加载该类的ClassLoader被回收

一般在大量使用反射,动态代理,CGLib等ByteCode框架,动态生成JSP以及OSGi这类频繁自定义的ClassLoader的场景都需要虚拟机能够回收无用的类功能,以保证永久代不会被溢出