Java 中的引用类型
为什么 JDK 中会设计这么多中引用类型呢?
首先,Java 是一门自带 GC 的语言,意味着我们不用像 C++ 一样手动管理内存,Java 会自动清除不需要的对象,但这样我们就无法控制对象的生命周期。
系统中存在大量的对象,其重要程度和生命周期是不同的。因此我们可以利用不同的引用类型控制 GC 回收对象的时间。
当内存空间还足够时,对象可以保留在内存中;内存空间在进行垃圾回收后仍然紧张,就可以抛弃某些对象释放内存。
JDK 1.2之后,Java 对引用对象进行了分类分为,从上到下引用类型越弱:
- Strong Reference(强引用)
- Soft Reference(软引用)
- Week Reference(弱引用)
- Phantom Reference(虚引用)
java.lang.ref 包结构:
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 | 无 |