前言
为了满足对不同情况的垃圾回收需求,Java从版本1.2开始,引入了4种引用类型(其实是额外增加了三种)的概念。本文将详细介绍这四种引用。
Java 4种引用类型
Java中的4中引用类型分别为强引用(String Reference),软引用(Soft Reference),弱引用(Weak Reference)和虚引用(Phantom Reference)。
概念及应用场景
- 强引用:Java中的引用,默认都是强引用。比如new一个对象,对它的引用就是强引用。对于被强引用指向的对象,就算JVM内存不足OOM,也不会去回收它们。
- 软引用:若一个对象只被软引用所引用,那么它将在JVM内存不足的时候被回收,即如果JVM内存足够,则软引用所指向的对象不会被垃圾回收(其实这个说法也不够准确,具体原因后面再说)。根据这个性质,软引用很适合做内存缓存:既能提高查询效率,也不会造成内存泄漏。
- 弱引用:若一个对象只被弱引用所引用,那么它将在下一次GC中被回收掉。如ThreadLocal和WeakHashMap中都使用了弱引用,防止内存泄漏。
- 虚引用:虚引用是四种引用中最弱的一种引用。我们永远无法从虚引用中拿到对象,被虚引用引用的对象就跟不存在一样。虚引用一般用来跟踪垃圾回收情况,或者可以完成垃圾收集器之外的一些定制化操作。Java NIO中的堆外内存(DirectByteBuffer)因为不受GC的管理,这些内存的清理就是通过虚引用来完成的。
引用队列
引用队列(Reference Queue)是一个链表,顾名思义,存放的是引用对象(Reference对象)的队列。
软引用与弱引用可以和一个引用队列配合使用,当引用所指向的对象被垃圾回收之后,该引用对象本身会被添加到与之关联的引用队列中,从而方便后续一些跟踪或者额外的清理操作。
因为无法从虚引用中拿到目标对象,虚引用必须和一个引用队列配合使用。
案例解析
设置JVM的启动参数为
-Xms10m -Xmx10m
public class ReferenceTest {
private static int _1MB = 1024 * 1024;
private static int _1KB = 1024;
public static void main(String[] args) throws InterruptedException {
// 引用队列,存放Reference对象
ReferenceQueue queue = new ReferenceQueue();
// 定义四种引用对象,强/弱/虚引用为1kb,软引用为1mb
Byte[] strong = new Byte[_1KB];
SoftReference<Byte[]> soft = new SoftReference<>(new Byte[_1MB], queue);
WeakReference<Byte[]> weak = new WeakReference<>(new Byte[_1KB], queue);
PhantomReference<Byte[]> phantom = new PhantomReference<>(new Byte[_1KB], queue);
Reference<String> collectedReference;
// 初始状态
System.out.println("Init: Strong Reference is " + strong);
System.out.println("Init: Soft Reference is " + soft.get());
System.out.println("Init: Weak Reference is " + weak.get());
System.out.println("Init: Phantom Reference is " + phantom.get());
do {
collectedReference = queue.poll();
System.out.println("Init: Reference In Queue is " + collectedReference);
}
while (collectedReference != null);
System.out.println("********************");
// 第一次手动触发GC
System.gc();
// 停100ms保证垃圾回收已经执行
Thread.sleep(100);
System.out.println("After GC: Strong Reference is " + strong);
System.out.println