内部类与垃圾回收:Java 8与新版Java的行为差异及变更疑问
Java非静态内部类的GC行为版本差异问题
面试时遇到这样一个问题:面试官展示了一段Java代码,询问它在Java 8和最新版Java(Java 23)中的输出结果。按照以往的理解,非静态内部类B持有外部类A的实例引用,所以只要B还存活,A就无法被垃圾回收(GC),这也是Java 8中的实际表现。但面试官演示在Java 23中A可以被GC回收,随后问这个变更始于哪个Java版本,以及背后的原因。
测试代码
class A { class B { @Override protected void finalize() throws Throwable { System.out.println("B destroy"); } } @Override protected void finalize() throws Throwable { System.out.println("A destroy"); } } public class Solution { public static void main(String[] args) throws InterruptedException { A a = new A(); A.B b = a.new B(); a = null; System.gc(); System.out.println("finish gc"); Thread.sleep(1000); System.out.println("finish sleep"); } }
不同版本输出
Java 8输出
finish gc finish sleep
Java 23输出
finish gc A destroy finish sleep
问题解答
- 变更起始版本:该行为变更始于Java 16。
- 原因解析:
在Java 16之前,不管非静态内部类有没有实际用到外部类的成员(字段、方法),都会持有外部类实例的强引用——这就导致只要内部类实例还活着,外部类实例就永远不会被GC回收。
从Java 16开始,编译器加入了针对性优化:如果非静态内部类完全没有访问外部类的任何成员,那么它对外部类实例的引用会被替换为弱引用。在这段测试代码里,B类只是重写了finalize方法,根本没用到A类的任何成员,所以B持有的是A的弱引用。当main方法把a置为null后,A实例就没有了任何强引用,GC就可以正常回收它,进而触发A destroy的输出。
内容的提问来源于stack exchange,提问作者maplemaple
相关产品推荐
相关产品推荐

