Java中的四种引用

众所周知,在Java中不需要程序员去主动管理内存,由JVM去负责内存的回收和分配。这样一来简化了程序员的工作,但是呢这也带来一个问题,就是不够灵活,垃圾回收对于程序员来说是不可控的。

在JDK1.2以前,如果一个对象不被任何变量引用,则程序无法再次使用这个对象,这个对象最终会被GC(GabageCollection:垃圾回收)。但是如果之后可能还会用到这个对象,就只能去新建一个了,这其实就降低了JVM性能,没有达到最大的优化策略。

因此,从JDK1.2开始,提供了四种类型的引用:强引用(StrongReference)、软引用(SoftReference)、弱引用(WeakReference)和虚引用(PhantomReference)。主要有两个目的:

  1. 可以在代码中决定某些对象的生命周期;
  2. 优化JVM的垃圾回收机制。

四种引用类型

强引用

强引用是最普遍的引用,如果一个对象具有强引用,那么即使内存不足(JVM抛出OutOfMemoryError),该对象也不会被回收。

1
2
// obj就是一个强引用
Object obj = new Object();

软引用

如果一个对象只具有软引用,当内存空间足够的时候,它就不会被回收,只有当内存不足的时候,

软引用才会被回收。JVM会优先回收长时间闲置不用的软引用的对象,对那些刚刚构建的或刚刚使用过的“新”软引用对象会尽可能保留。软引用可以和引用队列配合使用,如果一个对象即将被垃圾回收了,那么该软引用将会被添加到引用队列里。

1
2
3
4
5
6
7
8
9
10
11

Object obj = new Object();
SoftReference<Object> reference = new SoftReference<>(obj);
reference.get();

// 和引用队列配合使用
Object obj = new Object();
ReferenceQueue<Object> queue = new ReferenceQueue<>();
SoftReference<Object> reference = new SoftReference<>(obj, queue);
// 返回队列中的软引用并移除
queue.poll();

弱引用

持有弱引用的对象只能存活到下一次垃圾回收发生之前,无论内存空间是否足够,弱引用都会被回收。不过由于垃圾收集线程是一个优先级很低的线程,可能弱引用无法被及时回收。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Object obj = new Object();
ReferenceQueue<Object> queue = new ReferenceQueue<>();
WeakReference<Object> reference = new WeakReference<>(obj, queue);
System.out.println("obj:" + obj);
System.out.println("queue:" + queue.poll());
System.out.println("reference:" + reference);
System.out.println("reference.get():" + reference.get()); //不为null,其余和虚引用一样
obj = null;
//不要使用下一行代码,因为下一行代码会导致reference对象被GC回收,导致queue的poll()返回空
// reference = null; //这句话会导致下面的queue.poll()返回空,即reference对象都被回收了!!!
System.gc();
Thread.sleep(2000);
System.out.println("-----after gc-----------");
System.out.println("obj:" + obj);
// System.out.println("queue:" + queue.poll());
// poll方法返回 队列中的弱引用,并移除弱引用
if (queue.poll() != null) {
// obj对象即将被回收,执行业务逻辑,比如清理queue和reference对象
System.out.println("adfdcxd");
}
System.out.println("reference:" + reference);
System.out.println("reference.get():" + reference.get());

虚引用

与其它三种引用不同的是,虚引用不会决定对象的生命周期,如果一个对象持有虚引用,那么它就等于和没有任何引用一样,无法通过虚引用获取到该对象,在任何时候都可能被垃圾回收。虚引用主要作用是跟踪对象被垃圾回收的活动虚引用必须和引用队列联合使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Object obj = new Object();
ReferenceQueue<Object> queue = new ReferenceQueue<>();
PhantomReference<Object> reference = new PhantomReference<>(obj, queue);
System.out.println("obj:" + obj);
System.out.println("queue:" + queue.poll());
System.out.println("reference:" + reference);
System.out.println("reference.get():" + reference.get()); //为null,其余和虚引用一样
// 去除obj的强引用
obj = null;
//不要使用下一行代码,因为下一行代码会导致reference对象被GC回收,导致queue的poll()返回空
// reference = null; //这句话会导致下面的queue.poll()返回空,即reference对象都被回收了!!!
System.gc();
Thread.sleep(2000);
System.out.println("-----after gc-----------");
System.out.println("obj:" + obj);
// System.out.println("queue:" + queue.poll());
if (queue.poll() != null) {
// obj对象即将被回收,执行业务逻辑
}
System.out.println("reference:" + reference);
System.out.println("reference.get():" + reference.get());

我们可以通过判断引用队列中是 否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取相应的措施。

参考

  1. Java中的四种引用类型
  2. 深入理解Java虚拟机
显示评论