0x00 前言
JDK 1.2后,Java一共存在四种引用关系,分别是强引用、软引用、弱引用和虚引用。其中强引用是最常见的引用,代表程序运行所必须存在的引用,而软引用是非必须品,通常用于本地缓存。
0x01 软引用概述
当我们在程序中使用A a = new A()
时产生的就是一个强引用,强引用代表的程序的运行必须使用使用该对象,因此被强引用的对象不能被GC回收。而软引用不像强引用一样是必须品,当内存充足时,软引用会被保留,当内存紧张时,会优先回收无任何引用的对象,然后再考虑回收软引用。
0x02 软引用的使用
软引用需要被SoftReference对象包装,具体使用如下
SoftReference<A> sr = new SoftReference<>(new A());
上述代码中创建了一个A对象,但是没有创建A对象的强引用,而是创建了一个SoftReference对象对其进行软引用。具体的引用关系如下:
sr ——> SoftReference对象 ----> A对象
其中 ——> 代表强引用, ----> 代表软引用。
0x03 软引用的回收
软引用对象与其外围的SoftReference对象是一体的,当内存不足时软引用对象被回收后,其外围的SoftReference对象也同样应该被回收。
对于软引用的对象,它们会在内存不足时被GC自动回收,如果我们想要提前回收软引用对象,而不是等到内存不足时再回收,那么就可以考虑将SoftReference对象置为null,这样做删除了SoftReference的强引用,根据可达性分析,软引用对象同样需要被回收。代码示例如下:
public class MySoftReference {
public static void printMemoryUsage() {
long totalMemory = Runtime.getRuntime().totalMemory() >> 20;
long freeMemory = Runtime.getRuntime().freeMemory() >> 20;
long usedMemory = totalMemory - freeMemory;
System.out.println("Total Memory: " + totalMemory + " Mbytes");
System.out.println("Used Memory: " + usedMemory + " Mbytes");
}
public static void main(String[] args) throws IOException {
byte[] data = new byte[100 << 20];
printMemoryUsage();
SoftReference<byte[]> sr = new SoftReference<>(data);
data = null;
sr = null;
System.gc(); // request for GC
printMemoryUsage();
}
}
输出结果如下:
Total Memory: 192 Mbytes
Used Memory: 105 Mbytes
[GC (System.gc()) 106545K->103376K(196608K), 0.0009059 secs]
[Full GC (System.gc()) 103376K->641K(196608K), 0.0042565 secs]
Total Memory: 192 Mbytes
Used Memory: 1 Mbytes
注意这里我将JVM的堆内存上限改为了200MB,所以启动时只有192MB,当我们将sr置为null并且手动调用GC后,使用内存恢复到了1MB,说明软引用对象被回收。
除了直接将软引用对象置为null之外,Java也提供了另一种更加优雅的回收方式,当软引用对象被回收时,其外层的SoftReference对象会被放入到队列中,我们可以通过遍历队列来获取这些对象。代码示例如下,
ArrayList<SoftReference> softReferences = new ArrayList<>();
ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
for (int i = 0; i < 5; i++) {
byte[] data = new byte[100 << 20];
SoftReference<byte[]> reference = new SoftReference<>(data, queue);
softReferences.add(reference);
}
SoftReference<byte[]> ref = null;
while ((ref = (SoftReference<byte[]>) queue.poll()) != null) {
System.out.println(ref.get());
}
输出结果如下:
null
null
null
null