> 使用双花括号初始化集合,可别这么干 - Yuyy
Yuyy
Yuyy
使用双花括号初始化集合,可别这么干

Double Brace Initialization should not be used

前言

最近在修改sonar问题时,发现有人使用双花括号初始化集合,提示可能发生内存泄漏。这种初始化方式倒是见过,只知道是使用了匿名内部类,但没有意识到这个问题。

实测

A

提供两种Map的初始化方法,为了观察是否被回收,重写了finalize方法。

public class A {

    private String name;

    public A(String name) {
        this.name = name;
    }

    public Map<String, String> getMap() {
        return new HashMap<String, String>() {
            private static final long serialVersionUID = -3309655755403147761L;

            {
            put("name", name);
        }};

    }

    public Map<String, String> getMap1() {
        return new HashMap<String, String>();
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("Thread name: " + Thread.currentThread().getName() + " Object: " + this.name + " Gc happen");
        super.finalize();
    }
}

B

有个map成员变量

public class B {

    private Map<String, String> map;

    public B(Map<String, String> map) {
        this.map = map;
    }
}

Test

public class Test {
    public static void main(String[] args) throws InterruptedException {
        A a = new A("bob");
        final B b = new B(a.getMap());
        System.out.println("访问外部类对象的属性:" + b.getMap().get("name"));
        a = null;
        System.gc();
        Thread.sleep(1000);

        A a1 = new A("sandy");
        final B b1 = new B(a1.getMap1());
        a1 = null;
        System.gc();
        Thread.sleep(1000);
    }
}

输出

访问外部类对象的属性:bob
Thread name: Finalizer Object: sandy Gc happen

分析

匿名内部类持有外部类对象引用

双花括号初始化时,可以直接使用外部类对象的成员name。当然这只是表面,接下来我们从字节码的层面看看到底怎么回事。

使用了匿名内部类

  • 双花括号初始化的方法

http://bed.yuyy.info/image-20210910122144165.png

  • 非双花括号初始化的方法

http://bed.yuyy.info/image-20210910122213745.png

编译后产生的文件

http://bed.yuyy.info/image-20210910115942132.png

可以发现,多了一个内部类:A$1.class。

查看内部类字节码

http://bed.yuyy.info/image-20210910121241738.png

  1. 拥有一个外部类的成员变量
  2. 通过构造方法传入了外部类对象的引用
  3. 将外部类对象的引用赋值给成员变量
  4. 执行我们写的put方法
  5. 内部类继承于HashMap

发生了内存泄漏

使用非双花括号初始化map的sandy被回收了,而使用双花括号初始化map的bob却没有被回收。原因是b1的map和a1没啥关系,a1=null后,根不可达,所以被回收了。b的map持有a的引用,所以a=null后,还有强引用,依然根可达,不能回收,最终发生内存泄漏。

没有标签
首页      Java 基础      使用双花括号初始化集合,可别这么干

发表评论

textsms
account_circle
email

  • http://0.gravatar.com/avatar/0068e02261fe3b75e381a298da7dcf0c?s=80&d=monsterid&r=g
    Devin

    阿里老兵带我飞

    1周前回复

Yuyy

使用双花括号初始化集合,可别这么干
Double Brace Initialization should not be used 前言 最近在修改sonar问题时,发现有人使用双花括号初始化集合,提示可能发生内存泄漏。这种初始化方式倒是见过,只知道是使用了匿名…
扫描二维码继续阅读
2021-09-10
友情链接
标签
归档
近期文章