为何WeakReference未被GC清理,程序陷入无限运行?
这是个很典型的WeakReference使用误区!你代码的问题核心不是WeakReference本身没工作,而是JVM根本没触发垃圾回收——WeakReference只有在GC真正跑起来的时候,才会把没有强引用的对象标记为可回收,进而让get()返回null。
咱们一步步拆解问题:
先搞懂WeakReference的真实工作逻辑
WeakReference指向的对象,并不是只要没有强引用就立刻“消失”。它的核心规则是:当JVM执行垃圾回收时,发现某个对象仅被WeakReference引用(没有任何强引用),才会回收这个对象,同时把对应的WeakReference的get()返回值置为null。如果GC一直不执行,哪怕没有强引用了,对象也会好好待在内存里,WeakReference自然能一直拿到它。你的代码里GC为什么迟迟不触发?
你的程序占用的内存极小:一个简单的MyObj对象、几个轻量线程,完全没有大量内存分配的操作。JVM的GC触发是有明确阈值的(比如年轻代内存不足、老年代空间紧张等),你的程序根本没达到这些触发条件,JVM会觉得“内存还够得很,没必要费力气做GC”,所以就一直没执行回收操作。怎么验证这个结论?
你可以在第二个线程中,把m = null;之后加一行System.gc();(虽然生产环境不推荐依赖这个方法,但测试场景下足够验证问题),修改后的线程代码如下:new Thread(() -> { try { Thread.sleep(2500); System.out.println("# Will clear static reference to object (m = null)"); m = null; // 手动建议JVM执行垃圾回收 System.gc(); } catch (InterruptedException e) { throw new RuntimeException(e); } }).start();运行修改后的代码,你会发现第一个线程很快就会检测到
a.get() == null,程序会正常结束。额外提醒:别把System.gc()当生产方案
System.gc()只是向JVM“建议”执行GC,JVM完全可以忽略这个请求(不过在大多数桌面/开发环境下,它会响应)。生产环境中,你应该依赖JVM的自动GC策略,而不是手动调用。你的测试程序因为内存占用太低才出现GC不触发的情况,实际业务场景中如果有大量对象创建销毁,GC会自动触发,WeakReference也会正常工作。
总结一下:你的代码逻辑本身没问题,但因为JVM没触发GC,导致WeakReference指向的对象一直没被回收,所以线程才会无限循环下去~
备注:内容来源于stack exchange,提问作者noripcord

