为什么WeakHashMap中置为null的lambda表达式键未被GC回收?
问题原因分析
非捕获型Lambda的实例缓存机制
这是该现象的核心原因:
- Java虚拟机对无外部变量捕获的Lambda表达式有实例复用优化:你代码中的Lambda只用到了函数式接口自身的入参
callee1,没有引用方法内的其他局部变量、外部类字段等外部资源,属于典型的非捕获型Lambda。HotSpot虚拟机的实现中,这类Lambda会生成全局单例实例,由其对应生成的匿名类的静态字段强引用持有,这个静态引用的生命周期和类一致,只要类不被卸载,实例就永远存在强引用,不会被垃圾回收。 - 你代码中
key1和key2对应的Lambda实现逻辑完全一致,最终会指向同一个缓存实例(即key1 == key2结果为true),你仅把key1赋值为null,但key2仍然持有该实例的强引用,进一步保证了实例不会被GC清理。
System.gc()不保证强制触发GC
System.gc()只是给虚拟机发送了执行垃圾回收的建议,虚拟机完全可以选择忽略该请求,哪怕没有Lambda缓存的问题,也不能保证调用后立刻执行GC清理弱引用。
验证方法
你可以修改代码生成捕获型Lambda,比如在方法内新增一个局部变量,在Lambda中引用它,此时Lambda会变成捕获型,不会被全局缓存,就可以看到符合预期的回收效果:
@Test void callBackWithWeakHashMap() { Map<Callee.CallBack, String> map = new WeakHashMap<>(); // 新增局部变量让Lambda捕获,成为捕获型Lambda int flag = 1; Callee.CallBack key1 = callee1 -> System.out.println("input > " + callee1.getMsg() + flag); Callee.CallBack key2 = callee1 -> System.out.println("input > " + callee1.getMsg()); map.put(key1, "1"); map.put(key2, "2"); key1 = null; System.gc(); // 此时大概率只会输出key2对应的条目 map.entrySet().forEach(System.out::println); }
内容的提问来源于stack exchange,提问作者dooong dooong
相关产品推荐
相关产品推荐

