垃圾收集器进行垃圾回收的依据是判断对象是否“存活”,判断对象是否存活有两种方法。
在对象中添加一个引用计数器,每当有一个地方引用该对象时,计数器值加一;当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是已经“死亡”要被回收的。
引用计数法存在一个问题:对象之间的循环引用
public class ReferenceCountingTest { public Object instance = null; public static void main(String[] args) { ReferenceCountingTest a = new ReferenceCountingTest(); ReferenceCountingTest b = new ReferenceCountingTest(); a.instance = b; b.instance = a; a = null; b = null; // 假设在这行发生 GC,a和b是否能被回收? System.gc(); } }
在上述代码中,对象a
和对象b
都不可能再被访问,但是它们因为互相引用着对方,导致它们的引用计数都不为零,引用计算法将导致这两个对象无法被垃圾回收。
该算法通过一系列称为“GC Roots”的跟对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,如果某个对象没有直接或间接的与 GC Roots 相关联,则说明该对象是不可达的,将会被垃圾收集器回收。
无论通过哪种方法来判断对象的引用是否存在,判定对象是否存活都与“引用”有关。
在 JDK 1.2 之后,Java对引用的概念进行了扩充,将引用分为强引用、软引用、弱引用和虚引用。
被强引用所关联的对象,只要强引用关系还存在,就永远不会被垃圾收集器回收。
在 Java 代码中普遍存在的引用赋值,即Object obj = new Object()
这种引用关系都是强引用。
当内存空间不足时,JVM 会抛出OutOfMemoryError
异常来终止线程执行,也不会回收具有强引用关联的对象来解决内存不足的问题。
如果强引用对象不使用时,需要将对象弱化从而使 GC 能够回收
A a = null;
被软引用所关联的对象,只有在内存不足时才会被垃圾回收,这里的内存不足指的是已经发生了一次垃圾回收活动后,内存空间仍不足,此时就会触发第二次垃圾回收,回收到被软引用关联的对象。
Java 中可以使用SoftReference
类来创建一个被软引用关联的对象
// 创建软引用类型对象 SoftReference<Object> softReference = new SoftReference<>(new Object());
弱引用的强度低于软引用,无论内存是否足够,只要发生了垃圾回收,被弱引用所关联的对象就会被回收。
Java 中可以使用 WeakReference 类来创建一个被弱引用所关联的对象
// 创建弱引用类型对象 WeakReference<Object> weakReference = new WeakReference<>(new Object());
虚引用是最弱的一种引用类型,一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来创建一个对象。
为一个对象设置虚引用关联的唯一目的是为了能在该对象被收集器回收时收到一个系统通知。
在 JDK 1.2 之后可以使用 PhantomReference
类来实现虚引用
public class ReferenceType { @Test public void test1() { // 创建软引用类型对象 SoftReference<Object> softReference = new SoftReference<>(new Object()); Object o2 = softReference.get(); // 通过软引用来获取一个对象 System.out.println(o2); // java.lang.Object@20fa23c1 // 创建弱引用类型对象 WeakReference<Object> weakReference = new WeakReference<>(new Object()); Object o1 = weakReference.get(); // 通过弱引用来获取一个对象 System.out.println(o1); // java.lang.Object@20fa23c1 // 测试虚引用,虚引用必须和引用队列配合使用 ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>(); PhantomReference<Object> reference = new PhantomReference<>(new Object(), referenceQueue); Object o = reference.get(); // 通过虚引用来获取一个对象 System.out.println(o); // null;说明无法通过虚引用来获取一个对象 } }