在分析内存泄漏领域的问题时,时常会提到的就是非静态内部类持有外部类的引用导致的内存泄漏,因为内部类持有了外部类的引用,导致垃圾回收器无法回收外部类对象,久而久之就会存在内存泄漏的隐患。

我们在使用非静态内部类的时候,并不是说我们不手动编码引用外部类对象属性就不存在引用关系,实际上这层关系是通过隐式引用来实现的。

静态内部类持有外部类的引用

我们写一个非静态内部类对象示例:

/**
 * @author imyzt
 * @date 2024/05/13
 * @description 非静态内部类,匿名持有外部类的引用
 */
public class NonStaticInnerClazz {
    private final String name;
    public NonStaticInnerClazz(String name) {
        this.name = name;
    }
    public class Inner {
        public void print() {
            System.out.println(name);
        }
    }
}

可以看到javac之后的代码中,第九行方法形参将外部类NonStaticInnerClazz传入给了内部类的this.this$0属性进行引用。

非静态内部类无法引用外部类的属性

而非静态内部类则没有这种情况。

/**
 * @author imyzt
 * @date 2024/05/13
 * @description 静态内部类,无法引用外部类的属性
 */
public class StaticInnerClazz {
    private final String name;
    public StaticInnerClazz(String name) {
        this.name = name;
    }
    public static class Inner2 {
        public void print2() {
            // Non-static field 'name' cannot be referenced from a static context
            // System.out.println(name);
        }
    }
}

其他非静态内部类引用的情况

而其他非静态内部类的情况也有类似的问题:
编写测试类:

/**
 * @author imyzt
 * @date 2024/05/13
 * @description 描述信息
 */
public class NonStaticFieldClazz {
    // 成员变量-匿名内部类的非static实例
    private Inner3 inner3_1 = new Inner3() {
        private Integer field_111;
    };
    // 成员变量-非静态内部类的非static实例
    private Inner3 inner3_2 = new Inner3();
    public void print() {
        // 局部变量-匿名内部类的非static实例
        Inner3 inner3_3 = new Inner3() {
            private Integer field_333;
        };
        // 局部变量-非静态内部类的非static实例
        Inner3 inner3_4 = new Inner3();
    }
    public class Inner3 {
    }
}

1. 成员变量-匿名内部类的非static实例

class NonStaticFieldClazz$1 extends NonStaticFieldClazz.Inner3 {
    private Integer field_111;
    NonStaticFieldClazz$1(NonStaticFieldClazz var1) {
        super(var1);
        this.this$0 = var1;
    }
}

2. 局部变量-匿名内部类的非static实例

class NonStaticFieldClazz$2 extends NonStaticFieldClazz.Inner3 {
    private Integer field_333;
    NonStaticFieldClazz$2(NonStaticFieldClazz var1) {
        super(var1);
        this.this$0 = var1;
    }
}

3. 局部变量和成员变量的非static内部类的非static实例

public class NonStaticFieldClazz$Inner3 {
    public NonStaticFieldClazz$Inner3(NonStaticFieldClazz var1) {
        this.this$0 = var1;
    }
}

参考: