Java 中的引用类型

403 阅读2分钟

Java 中的引用类型

为什么 JDK 中会设计这么多中引用类型呢?

首先,Java 是一门自带 GC 的语言,意味着我们不用像 C++ 一样手动管理内存,Java 会自动清除不需要的对象,但这样我们就无法控制对象的生命周期。

系统中存在大量的对象,其重要程度和生命周期是不同的。因此我们可以利用不同的引用类型控制 GC 回收对象的时间。

当内存空间还足够时,对象可以保留在内存中;内存空间在进行垃圾回收后仍然紧张,就可以抛弃某些对象释放内存。

JDK 1.2之后,Java 对引用对象进行了分类分为,从上到下引用类型越弱:

  • Strong Reference(强引用)
  • Soft Reference(软引用)
  • Week Reference(弱引用)
  • Phantom Reference(虚引用)

java.lang.ref 包结构

Package ref.png

ReferenceQueue

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashMap;

public class Main {
    public static void main(String[] args) {
        ReferenceQueue<Integer[]> queue = new ReferenceQueue<>();

        // 产生大对象,使 GC 清理对象
        new Thread(() -> {
            HashMap<WeakReference<Integer[]>, Integer> map = new HashMap<>();
            for (int i = 0; i < 10000; i++) {
                map.put(new WeakReference<>(new Integer[1024 * 1024], queue), i);
            }
        }).start();

        // 监控清理的对象,此时对象已经被清理,只剩下 WeakReference 引用
        new Thread(() -> {
            Reference<? extends Integer[]> ref;
            while (true) {
                try {
                    while ((ref = queue.remove()) != null) {
                        System.out.printf("对象引用:%s, 对象:%s\n", ref, ref.get());
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

当引用的对象被清除的时候,会把用于包装对象的 Reference(Week, Soft, Phantom) 传入到 ReferenceQueue 中,后续可从队列中获取,进行后续处理 Reference ,比如判断是否发生内存泄漏等。

StrongReference

public class Main {
    public static void main(String[] args) {
        Object o = new Object();
    }
}

当我们利用 new 新建对象时 = 就是强引用,只要引用关系存在,GC 就不会清除被引用的对象。

SoftReference

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;

public class Main {
    public static void main(String[] args) {
        ReferenceQueue<Object> refQ = new ReferenceQueue<>();
        Object o = new Object();
        // 创建对象 o 的软引用
        SoftReference<Object> refOne = new SoftReference<>(o);
        // 创建对象 o 的软引用,并传入 ReferenceQueue,当被清除时会传入队列中
        SoftReference<Object> refTwo = new SoftReference<>(o, refQ);
    }
}

软引用用于描述还有些用,但非必须的对象。

在虚拟机抛出 OutOfMemoryError 之前,所有对软可达对象的软引用都保证已被清除。

只要软引用的引用是强可达(对象此时有强引用链)的,也就是说,它实际上是在使用的,软引用就不会被清除。

import java.lang.ref.SoftReference;

public class Main {
    public static void main(String[] args) {
        Object o = new Object();
        SoftReference<Object> refOne = new SoftReference<>(o);
        // 此时对象 o 有强引用,因此是强可达的
        Object otherO = o;
    }
}

WeakReference

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;

public class Main {
    public static void main(String[] args) {
        ReferenceQueue<Object> refQ = new ReferenceQueue<>();

        Object o = new Object();
        WeakReference<Object> refOne = new WeakReference<>(o);
        WeakReference<Object> refTwo = new WeakReference<>(o, refQ);
    }
}

弱引用描述那些非必需的对象,但强度比 SoftReference 更弱的对象,在下一次 GC 时无论当前内存是足够,都会清除只包含弱引用链的对象。

PhantomReference

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue<Object> refQ = new ReferenceQueue<>();

        Object o = new Object();
        PhantomReference<Object> ref = new PhantomReference<>(o, refQ);

        // 建议 GC
        o = null;
        System.gc();

        // 等待获取被 GC 清除的对象
        Reference<?> reference = refQ.remove();
        System.out.println(ref);
        System.out.println(reference);
    }
}

// answer
java.lang.ref.PhantomReference@59a6e353
java.lang.ref.PhantomReference@59a6e353

也可以叫做 虚幻引用 或者 幻影引用,最弱的一种引用关系。

是否存在虚引用不会影响其生命周期,也无法取得对象实例。

唯一作用是在对象被清除的时候接收到系统的通知。

四种引用比较

引用类型清除时间
StrongReference强引用关系结束时
SoftReference内存不足够时
WeakReference在下一次 GC 发生时
PhantomReference