前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用双花括号初始化集合,可别这么干

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

作者头像
Yuyy
发布2022-09-21 10:05:44
9280
发布2022-09-21 10:05:44
举报
文章被收录于专栏:yuyy.info技术专栏

Double Brace Initialization should not be used

前言

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

实测

A

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

代码语言:javascript
复制
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成员变量

代码语言:javascript
复制
public class B {

    private Map<String, String> map;

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

Test

代码语言:javascript
复制
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);
    }
}

输出

代码语言:javascript
复制
访问外部类对象的属性:bob
Thread name: Finalizer Object: sandy Gc happen

分析

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

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

使用了匿名内部类
  • 双花括号初始化的方法
image-20210910122144165
image-20210910122144165
  • 非双花括号初始化的方法
image-20210910122213745
image-20210910122213745
编译后产生的文件
image-20210910115942132
image-20210910115942132

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

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

发生了内存泄漏

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-9-10 1,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Double Brace Initialization should not be used
    • 前言
      • 实测
        • A
        • B
        • Test
        • 输出
      • 分析
        • 匿名内部类持有外部类对象引用
        • 发生了内存泄漏
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档