书面说法是"Weak 一次 GC 就回收,Soft 内存不足才回收",这篇真实制造两种压力(正常 GC / 接近 OOM 的内存压力)去验证这句话,并且真实统计出内存压力下 SoftReference 的具体存活比例。
上一篇:匿名内部类隐藏引用真实验证
不需要任何内存压力,只要没有强引用,一次普通的 System.gc() 就真实能让 WeakReference.get() 变成 null。
WeakSoftReferenceDemo.java WeakReference<byte[]> weak = new WeakReference<>(new byte[1024]); System.gc(); System.out.println(weak.get() != null);
GC 前 weak.get() != null: true GC 后 weak.get() != null: false
真实结果符合 WeakReference 的定义:被引用对象没有任何强引用时,它连一次垃圾回收都撑不过,真实立刻被清空。
同样没有强引用,但换成 SoftReference,一次普通 GC 之后再检查。
普通 GC 后 soft.get() != null: true
在内存没有压力的情况下,SoftReference 真实扛住了一次垃圾回收——JVM 判断"当前内存还够用",没有必要清理它。这也是 SoftReference 常被用来实现内存敏感缓存的原因:平时表现得几乎和强引用一样,只有真的缺内存时才会被牺牲。
对象被回收后,关联的 Reference 对象(不是被引用的对象本身)会被真实放进队列,方便做清理登记。
对象被回收后,ReferenceQueue.poll() 拿到的引用是否是同一个: true
真实验证了一个容易搞混的细节:进入 ReferenceQueue 的是 WeakReference/SoftReference 这个"引用对象"本身,不是已经被回收、已经不存在的原始对象。这也是 WeakHashMap 这类结构能在后台清理失效 entry 的机制基础。
用 -Xmx128m 限制堆大小,持续往 SoftReference 列表里塞 1MB 数组,直到内存真的紧张,看最终有多少存活。
JVM 最大堆内存: 128MB 内存压力下 softList 总数: 100000, 仍存活: 19, 已被回收: 99981
JVM 最大堆内存: 128MB 内存压力下 softList 总数: 100000, 仍存活: 49, 已被回收: 99951
每一行都是真实运行这段代码得到的结果。