软引用
软引用是使用SoftReference创建的引用,强度弱于强引用,被其引用的对象在内存不足的时候会被回收,不会产生内存溢出。
软引用,顾名思义就是比较“软”一点的引用。
当一个对象与GC Roots之间存在强引用时,无论何时都不会被GC回收掉。如果一个对象与GC Roots之间没有强引用与其关联而存在软引用关联时,那么垃圾回收器对它的态度就取决于内存的紧张程度了。如果内存空间足够,垃圾回收器就不会回收这个对象,但如果内存空间不足了,它就难逃被回收的厄运。
如果一个对象与GC Roots之间不存在强引用,但是存在软引用,则称这个对象为软可达(soft reachable)
对象。
在垃圾回收器没有回收它的时候,软可达对象就像强可达对象一样,可以被程序正常访问和使用,但是需要通过软引用对象间接访问,需要的话也能重新使用强引用将其关联。所以软引用适合用来做内存敏感的高速缓存。
String s = new String("Frank"); // 创建强引用与String对象关联,现在该String对象为强可达状态
SoftReference<String> softRef = new SoftReference<String>(s); // 再创建一个软引用关联该对象
s = null; // 消除强引用,现在只剩下软引用与其关联,该String对象为软可达状态
s = softRef.get(); // 重新关联上强引用
这里变量s持有对字符串对象的强引用,而softRef持有对该对象的软引用,所以当执行s = null后,字符串对象就只剩下软引用了,这时如果因为内存不足发生Full GC,就会把这个字符串对象回收掉。
注意,在垃圾回收器回收一个对象前,SoftReference类所提供的get方法会返回Java对象的强引用,一旦垃圾线程回收该对象之后,get方法将返回null。所以在获取软引用对象的代码中,一定要先判断返回是否为null,以免出现NullPointerException异常而导致应用崩溃。
下面的代码会让s再次持有对象的强引用:
s = softRef.get();
如果在softRef指向的对象被回收前,用强引用指向该对象,那这个对象又会变成强可达。
来看一个使用SoftReference的栗子:
public class TestA {
static class OOMClass{
private int[] oom = new int[1024 * 100];// 100KB
}
public static void main(String[] args) throws InterruptedException {
ReferenceQueue<OOMClass> queue = new ReferenceQueue<>();
List<SoftReference> list = new ArrayList<>();
while(true){
for (int i = 0; i < 100; i++) {
list.add(new SoftReference<OOMClass>(new OOMClass(), queue));
}
Thread.sleep(500);
}
}
}
注意,ReferenceQueue中声明的类型为OOMClass,即与SoftReference引用的类型一致。