• 我的订阅
  • 科技

如何避免在 Java 中使用双括号初始化

类别:科技 发布时间:2023-07-12 14:00:00 来源:CSDN

【编者按】本文介绍了一个使用了 Java 的双括号初始化语法导致内存泄漏的案例。作者分析了泄漏的原因,提出了几种解决的方法,并给出了代码示例。

链接:https://blog.p-y.wtf/avoid-java-double-brace-initialization

作者 | Pierre-Yves Ricau责编 | 明明如月

责编 | 夏萌

出品 | CSDN(ID:CSDNnews)

如何避免在 Java 中使用双括号初始化

结论先行

避免像这样,在 Java 中使用双括号初始化:newHashMap< String, String> {{ put( "key", value); }};

内存泄漏追踪

我最近正在 LeakCanary看到了以下内存泄漏追踪信息:┬───│ GC Root: Global variable innativecode │├─ com.bugsnag.android.AnrPlugin instance│ Leaking: UNKNOWN│ ↓ AnrPlugin.client│ ~~~~~~├─ com.bugsnag.android.Client instance│ Leaking: UNKNOWN│ ↓ Client.breadcrumbState│ ~~~~~~~~~~~~~~~├─ com.bugsnag.android.BreadcrumbState instance│ Leaking: UNKNOWN│ ↓ BreadcrumbState.store│ ~~~~~├─ com.bugsnag.android.Breadcrumb[] array│ Leaking: UNKNOWN│ ↓ Breadcrumb[ 494] │ ~~~~~├─ com.bugsnag.android.Breadcrumb instance│ Leaking: UNKNOWN│ ↓ Breadcrumb.impl│ ~~~~├─ com.bugsnag.android.BreadcrumbInternal instance│ Leaking: UNKNOWN│ ↓ BreadcrumbInternal.metadata│ ~~~~~~~~├─ com.example.MainActivity$ 1instance │ Leaking: UNKNOWN│ Anonymous subclass of java.util.HashMap│ ↓ MainActivity$ 1. this$ 0│ ~~~~~~╰→ com.example.MainActivity instanceLeaking: YES (Activity#mDestroyed istrue)

当打开一个内存泄漏追踪日志时,我首先会看底部的对象,了解它的生命周期,这将帮助我理解内存泄漏追踪中的其他对象是否应该有相同的生命周期。

在底部,我们看到:╰→ com.example.MainActivityinstanceLeaking: YES( Activity#mDestroyedistrue)

Activity已经被销毁,应该已被垃圾回收器给回收掉了,但它仍驻留在内存中。

此时,我开始在内存泄漏追踪日志中寻找已知类型,并尝试弄清楚它们是否属于同一个被销毁的范围(=> 正在泄漏)或更高的范围(=> 没有泄漏)。

在顶部,我们看到:├─ com.bugsnag.android.Clientinstance│ Leaking: UNKNOWN

我们的 BugSnag客户端是一个用于分析崩溃报告单例,由于每个应用我们创建一个实例,所以它没有泄漏。├─ com.bugsnag.android.Clientinstance│ Leaking: NO

所以我们现在需要转变焦点,特别关注从最后一个 Leaking: NO到第一个 Leaking: YES的部分:…├─ com.bugsnag.android.Client instance│ Leaking: NO│ ↓ Client.breadcrumbState│ ~~~~~~~~~~~~~~~├─ com.bugsnag.android.BreadcrumbState instance│ Leaking: UNKNOWN│ ↓ BreadcrumbState.store│ ~~~~~├─ com.bugsnag.android.Breadcrumb[] array│ Leaking: UNKNOWN│ ↓ Breadcrumb[494]│ ~~~~~├─ com.bugsnag.android.Breadcrumb instance│ Leaking: UNKNOWN│ ↓ Breadcrumb.impl│ ~~~~├─ com.bugsnag.android.BreadcrumbInternal instance│ Leaking: UNKNOWN│ ↓ BreadcrumbInternal.metadata│ ~~~~~~~~├─ com.example.MainActivity $1instance │ Leaking: UNKNOWN│ Anonymous subclass of java.util.HashMap│ ↓ MainActivity $1.this $0│ ~~~~~~╰→ com.example.MainActivity instanceLeaking: YES (Activity #mDestroyed is true)

BugSnag 客户端保持了一个面包屑的环形缓冲区。这些应该保留在内存中,它们也没有泄漏。

所以让我们跳过上述内容,从下面这里继续分析:├─ com.bugsnag.android.BreadcrumbInternalinstance│ Leaking: NO

我们只需要关注从最后一个 Leaking: NO到第一个Leaking: YES的部分:…├─ com.bugsnag.android.BreadcrumbInternal instance│ Leaking: NO│ ↓ BreadcrumbInternal.metadata│ ~~~~~~~~├─ com.example.MainActivity $1instance │ Leaking: UNKNOWN│ Anonymous subclass of java.util.HashMap│ ↓ MainActivity $1.this $0│ ~~~~~~╰→ com.example.MainActivity instanceLeaking: YES (Activity #mDestroyed is true)BreadcrumbInternal.metadata :内存泄漏追踪通过面包屑实现的元数据字段。

也就是说:记录到 BugSnag 的面包屑之一有一个元数据映射,这是一个 HashMap的匿名子类 ,它保留对外部类的引用,这个外部类就是被销毁的 Activity 。

让我们看看我们在 MainActivity中记录面包屑的地方:voidlogSavingTicket( StringticketId) { Map< String, Object> metadata = newHashMap< String, Object> {{ put( "ticketId", ticketId); }};bugsnagClient.leaveBreadcrumb( "Saving Ticket", metadata, LOG); }

这段代码利用了一个被称为“双括号初始化” 的有趣的 Java 代码块 。它允许你创建一个 HashMap,并通过添加代码到HashMap的匿名子类的构造函数中同时初始化它。newHashMap< String, Object> {{ put( "ticketId", ticketId); }};

Java 的匿名类总是隐式地引用其外部类。

因此,这段代码:voidlogSavingTicket( StringticketId) { Map< String, Object> metadata = newHashMap< String, Object> {{ put( "ticketId", ticketId); }};bugsnagClient.leaveBreadcrumb( "Saving Ticket", metadata, LOG); }

实际上被编译为:classMainActivity$1 extendsHashMap< String, Object> { private final MainActivity this$ 1;

MainActivity$ 1(MainActivity this$ 1, StringticketId) { this.this$ 1= this$ 1; put( "ticketId", ticketId); }}

voidlogSavingTicket( StringticketId) { Map< String, Object> metadata = newMainActivity$ 1( this, ticketId); bugsnagClient.leaveBreadcrumb( "Saving Ticket", metadata, LOG); }

结果,这个 breadcrumb 就一直持有对已销毁的 activity 实例的引用。

总结

尽管使用 Java 的双括号初始化看起来很"炫酷",但它会无故地额外创建类,可能会导致内存泄漏。因此避免在 Java 中使用双括号初始化。

你可以用下面这种更安全的方式来解决这个问题:Map< String, Object> metadata = newHashMap<>; metadata.put( "ticketId", ticketId); bugsnagClient.leaveBreadcrumb( "Saving Ticket", metadata, LOG);

或者利用 Collections.singletonMap进一步简化代码:Map< String, Object> metadata = singletonMap( "ticketId", ticketId); bugsnagClient.leaveBreadcrumb( "Saving Ticket", metadata, LOG);

或者,直接将文件转换为 Kotlin。

你是否在使用 Java 时遇到过内存泄漏的问题?

▶短短 5 天,Python 开发的“Twitter 杀手”Threads 用户过亿,增幅猛超 ChatGPT,马斯克暴怒!

▶ 开源商业化,走出“射手假说”迷雾

▶ ChatGPT 点燃向量数据库赛道,刚刚,Zilliz Cloud 云服务重磅发布! 返回搜狐,查看更多

责任编辑:

以上内容为资讯信息快照,由td.fyun.cc爬虫进行采集并收录,本站未对信息做任何修改,信息内容不代表本站立场。

快照生成时间:2023-07-12 15:45:09

本站信息快照查询为非营利公共服务,如有侵权请联系我们进行删除。

信息原文地址:

微软发布 Visual Studio 2022 17.6更新
...程语言的显示效果,帮助开发人员更好地直观地区分左大括号和右大括号集,以便更轻松地找到缺失的大括号。 新版本中引入了用于C#、C++和Markdown文件的拼写检查器
2023-05-18 11:41:00
当毛衣遇上括号裙,轻松彰显不俗品味
“括号裙”是什么呢?其实就是一种形状像括号的裙子,因为上下两侧和中间相比略窄一些,所以看上去很是形似括号。这种“括号裙”最大的优势就是百搭不挑人,还能够修饰身材曲线,使得整个人看
2023-08-30 19:14:00
英文输入法状态下的大括号键,可以快速调节画笔大小
...调节画笔大小,就能操作瘦脸了。英文输入法状态下的大括号键,可以快速调节画笔大小。其中,左大括号键是缩小,右大括号键是放大。大家把画笔移动到人物脸部边缘,按住鼠标左键往里拖动,
2023-01-08 20:23:00
如何绘制「UML类图」?附内容详解和优质实例分析!
...示默认值下划线:static斜体:抽象 (注意也可以用两个尖括号包裹来表示抽象,比如 —— <>)冒号前是方法名/变量名(根据有无括号区分)
2023-10-26 18:02:00
反复流产或胚停,到底该怎么办?
...。这种情况在临床中很常见,也困扰着很多夫妇。01左中括号什么是复发性流产?左中括号我国通常将3次或3次以上在妊娠28周之前的胎儿丢失称为复发性流产。但许多专家认为,连续发生2
2023-11-09 08:47:00
梦想当“燃” | 浪潮KaiwuDB研发团队:因为高能,让不可能,成为可能
...下这支“高能团队”以志为马,逐梦前行的故事。01左中括号敢想,就有可能左中括号2019年,KaiwuDB初创团队敏锐地捕捉到了软件服务市场的新趋势。随着数字经济纳入国家战略,
2023-08-24 11:18:00
每天扔掉一半也不降价,中国面包店为何成了奢侈品?为何不打折?
...度。面包店可以尝试将未售出的面包用于开发新产品,如面包屑、面包干等。这样不仅能减少浪费,还能增加产品种类,满足消费者的多样化需求。通过优化生产和库存管理,更精确地预测市场需求
2024-05-27 11:53:00
面包屑导航什么时候不能用?
本篇文章将详细介绍面包屑导航的应用场景及其在B端设计的应用,能给产品设计的伙伴们提供一些参考思路,希望能对你有所启发。在我们的日常互联网使用过程中,面包屑导航无处不在。从电商网站
2023-07-09 18:00:00
为什么你总是搜索不到想要的资料?
...索”但是不包含“引擎”。使用减号搜索不使用减号搜索括号搜索我们在使用浏览器搜索资料时,搜索引擎会对输入的关键词进行自动分词,然后根据分词自动搜索内容返回给用户,如果将关键词加
2023-02-20 20:40:00
更多关于科技的资讯: