Java SoftReference在OOM前未回收?两种实现差异解惑
问题背景
测试代码1及输出
public class Test { public static SoftReference<byte[]> cache = new SoftReference<>(new byte[0]); public static List<byte[]> list = new ArrayList<>(); public static void main(String[] args) { try { func(); } catch (OutOfMemoryError e) { sniff(); e.printStackTrace(); } } public static void func() { byte[] bytes = new byte[1024 * 1024]; cache = new SoftReference<>(bytes); for(;;) { byte[] tmp = new byte[1024 * 1024]; list.add(tmp); } } public static void sniff() { byte[] bytes = cache.get(); if (bytes == null) { System.out.println("recycling data."); } else { System.out.println("object still live"); } } }
输出:
object still live
测试代码2及输出
public class Test { public static SoftReference<byte[]> cache = new SoftReference<>(new byte[0]); public static List<byte[]> list = new ArrayList<>(); public static void main(String[] args) { try { func(); } catch (OutOfMemoryError e) { sniff(); e.printStackTrace(); } } public static void func() { for(;;) { byte[] tmp = new byte[1024 * 1024]; list.add(tmp); byte[] bytes = new byte[1024 * 1024]; cache = new SoftReference<>(bytes); } } public static void sniff() { byte[] bytes = cache.get(); if (bytes == null) { System.out.println("recycling data."); } else { System.out.println("object still live"); } } }
输出:
recycling data.
根据Oracle官方文档描述:
All soft references to softly-reachable objects are guaranteed to have been cleared before the virtual machine throws an OutOfMemoryError.
用户疑问:
- 第一种写法中,垃圾回收器为何未回收SoftReference指向的对象?
- 为何两种写法会产生如此差异?
问题解答
1. 第一种写法未回收的原因
核心是软引用指向的byte[]对象仍处于强可达状态:
在第一种写法的func方法中,byte[] bytes是方法级局部变量,当代码进入无限for循环后,func方法并未执行完毕,这个bytes变量始终存在于当前栈帧的局部变量表中——也就是说,该byte[]对象有一个强引用(bytes变量)还在生效。
软引用的回收规则是:仅当对象是软可达(无任何强引用指向,仅被软引用关联)时,GC才会在OOM发生前优先回收这类对象。由于第一种情况里对象是强可达的,GC没有回收它的理由,所以cache.get()能拿到对象,输出object still live。
2. 两种写法的差异根源
第二种写法把byte[] bytes = new byte[1024 * 1024]; cache = new SoftReference<>(bytes);放进了for循环体内部:
- 每次循环结束后,当前迭代的
bytes局部变量会随循环体栈帧销毁而失效,对应的强引用消失; - 新循环迭代会创建新的
bytes变量和byte[]对象,同时更新cache指向新对象——此时旧的byte[]对象仅剩下软引用关联,成为软可达状态; - 内存耗尽触发OOM前,GC会清理所有软可达对象,最后
cache指向的最后一次循环创建的对象,也因循环结束后强引用消失被回收,所以cache.get()返回null,输出recycling data.。
内容的提问来源于stack exchange,提问作者Colin
相关产品推荐
相关产品推荐

