四种引用介绍
# 四种引用介绍
# 1. 四种引用介绍
- 强引用(Strong Reference):大部分的对象的引用都是强引用,new 出来的对象就是强引用类型,强引用的对象是必不可少的存在,垃圾回收不会回收这些对象,即使内存空间不足,jvm 宁可抛出 OutOfMemeryError 错误,使程序终止,也不会回收强引用对象。
- 软引用(Soft Reference):当内存空间足够的时候,垃圾回收不会回收这些对象;当内存空间不足的时候,垃圾回收就会回收这些对象。使用
SoftReference
修饰。 - 弱引用(Weak Reference):当进行垃圾回收是,不管内存是否足够,都会将弱引用对象进行回收。使用
WeakReference
修饰。通常用于实现对象的辅助数据结构,当对象不再被强引用引用时,可以通过弱引用来获取对象的相关信息。 - 虚引用(Phantom Reference):他不能单独使用,必须和引用队列联合使用,虚引用主要用来跟踪对象被垃圾回收的活动。
# 2. 软引用
当内存空间足够的时候,垃圾回收不会回收这些对象;当内存空间不足的时候,垃圾回收就会回收这些对象。
🖥 代码
SoftReference<Object> softReference = new SoftReference<>(new Object());
System.out.println(softReference.get()); // 输出对象地址
try {
byte[] bytes = new byte[30 *1024 *1024];
} finally {
System.out.println(softReference.get()); // null
}
2
3
4
5
6
7
📃 使用场景
假如一个应用需要读取大量的图片,如果每次读取图片都从硬盘读取则会严重影响性能,如果一次性全部加载到内存中有可能造成内存溢出。
设计思路:用一个HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象占用的空间,从而有效避免了OOM的问题。
Map<String, SoftReference<Bitmap>> imageCache = new HashMap<>();
# 3. 弱引用
当进行垃圾回收是,不管内存是否足够,都会将弱引用对象进行回收。
🖥 代码
WeakReference<Object> weakReference = new WeakReference<>(new Object());
System.out.println(weakReference.get()); // 输出对象地址
System.gc();
System.out.println(weakReference.get()); // null
2
3
4
🌰 应用场景:WeakHashMap、ThreadLocal
WeakHashMap使用了弱引用,当entry键值对中的键不再被引用的时候,在垃圾回收的时候,这个entry就会被垃圾回收。
Integer key = new Integer(1);
String value = "s";
Map<Integer, String> map = new WeakHashMap<>();
map.put(key, value);
System.out.println(key); // 1
System.out.println(map); // {1=s}
System.out.println("-----------------------------");
key = null;
System.out.println(key); // null
System.out.println(map); // {1=s}
System.out.println("-----------------------------");
System.gc();
System.out.println(key); // null
System.out.println(map); // {} 如果是HashMap,这里就为{1=s}
System.out.println("-----------------------------");
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ThreadLocal 也同样应用了弱引用,TreadLocalMap 中的 Entry 的键就是弱引用。
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
2
3
4
5
6
7
8
9
# 4. 虚引用
虚引用不会决定对象的生命周期,如果一个对象持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收,他不能单独使用,也不能通过它访问对象,虚引用必须和引用队列ReferenceQueue
联合使用。
虚引用的作用是跟踪对象被垃圾回收的状态。仅仅提供了一种确保对象被finalize之后,做某些事情的机制。
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
PhantomReference<Object> phantomReference = new PhantomReference<>(new Object(), referenceQueue);
System.out.println(phantomReference.get()); // null
System.out.println(referenceQueue.poll()); // null
System.out.println("----------------------------");
System.gc();
Thread.sleep(500);
System.out.println(phantomReference.get()); // null
System.out.println(referenceQueue.poll()); // java.lang.ref.PhantomReference@1b6d3586
2
3
4
5
6
7
8
9
10
11
12
构造时必须传入引用队列:
public PhantomReference(T referent, ReferenceQueue<? super T> q)
虚引用和引用队列关联使用,当垃圾回收器准备回收一个对象时,如果发现它还是虚引用,就会把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否要将垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,就可以在所引用的对象的内存被回收之前采取必要的行动。
# 5. 引用队列
对象被回收前需要被引用队列保存,下面以弱引用为例:
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
WeakReference<Object> weakReference = new WeakReference<>(new Object(), referenceQueue);
System.out.println(weakReference.get()); // java.lang.Object@1b6d3586
System.out.println(referenceQueue.poll()); // null
System.out.println("----------------------------");
System.gc();
Thread.sleep(500);
System.out.println(weakReference.get()); // null
System.out.println(referenceQueue.poll()); // java.lang.ref.WeakReference@4554617c
2
3
4
5
6
7
8
9
10
11
12
引用和引用队列的继承关系