java语言之基本语法有哪些(如何避免在 Java 中使用双括号初始化人和猩猩能不能生出孩子?前苏联的‘人兽杂交’实验揭秘!)

wufei123 发布于 2024-01-21 阅读(176)

【编者按】本文介绍了一个使用了 Java 的双括号初始化语法导致内存泄漏的案例作者分析了泄漏的原因,提出了几种解决的方法,并给出了代码示例链接:https://blog.p-y.wtf/avoid-java-double-brace-initialization。

未经允许,禁止转载!作者 | Pierre-Yves Ricau责编 | 明明如月责编 | 夏萌出品 | CSDN(ID:CSDNnews)

广告胆小者勿入!五四三二一...恐怖的躲猫猫游戏现在开始!×结论先行避免像这样,在 Java 中使用双括号初始化:new HashMap() {{ put("key"

, value);}};内存泄漏追踪我最近正在 LeakCanary 看到了以下内存泄漏追踪信息:┬───│ GC Root: Global variable innative code│├─ 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$

1 instance│ Leaking: UNKNOWN│ Anonymous subclass of java.util.HashMap│ ↓ MainActivity$1.this$0│ ~~~~~~

╰→ com.example.MainActivity instance Leaking: YES (Activity#mDestroyed istrue)当打开一个内存泄漏追踪日志时,我首先会看底部的对象,了解它的生命周期,这将帮助我理解内存泄漏追踪中的其他对象是否应该有相同的生命周期。

在底部,我们看到:╰→ com.example.MainActivityinstanceLeaking: YES (Activity#mDestroyedistrue)Activity 已经被销毁,应该已被垃圾回收器给回收掉了,但它仍驻留在内存中。

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

instance│ 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

$1 instance│ Leaking: UNKNOWN│ Anonymous subclass of java.util.HashMap│ ↓ MainActivity$1.this$0

│ ~~~~~~╰→ com.example.MainActivity instance Leaking: YES (Activity#mDestroyed is true)

BugSnag 客户端保持了一个面包屑的环形缓冲区这些应该保留在内存中,它们也没有泄漏所以让我们跳过上述内容,从下面这里继续分析:├─ com.bugsnag.android.BreadcrumbInternal。

instance│ Leaking: NO我们只需要关注从最后一个 Leaking: NO 到第一个Leaking: YES 的部分:…├─ com.bugsnag.android.BreadcrumbInternal instance

│ Leaking: NO│ ↓ BreadcrumbInternal.metadata│ ~~~~~~~~├─ com.example.MainActivity

$1 instance│ Leaking: UNKNOWN│ Anonymous subclass of java.util.HashMap│ ↓ MainActivity$1.this$0

│ ~~~~~~╰→ com.example.MainActivity instance Leaking: YES (Activity#mDestroyed is true)

BreadcrumbInternal.metadata :内存泄漏追踪通过面包屑实现的元数据字段MainActivity$1 实例是 java.util.HashMap 的匿名子类:MainActivity$1。

是在MainActivity 中定义的 HashMap 的匿名子类它是从 MainActivity.java 中定义的第一个匿名类(因为是 $1 )this$0:每个匿名类都有一个隐式字段引用到定义它的外部类,这个字段被命名为。

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

MainActivity 中记录面包屑的地方:void logSavingTicket(String ticketId) {Map metadata = new HashMap<

String, Object>() {{ put("ticketId", ticketId); }}; bugsnagClient.leaveBreadcrumb("Saving Ticket"

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

new HashMap() {{ put("ticketId", ticketId);}};Java 的匿名类总是隐式地引用其外部类因此,这段代码:void logSavingTicket(。

String ticketId) {Map metadata = new HashMap() {{ put("ticketId"

, ticketId); }}; bugsnagClient.leaveBreadcrumb("Saving Ticket", metadata, LOG);}实际上被编译为:classMainActivity

$1 extendsHashMap { private final MainActivity this$1; MainActivity$1(MainActivity

this$1, String ticketId) {this.this$1 = this$1; put("ticketId", ticketId); }}void logSavingTicket(

String ticketId) {Map metadata = new MainActivity$1(this, ticketId); bugsnagClient.leaveBreadcrumb(

"Saving Ticket", metadata, LOG);}结果,这个 breadcrumb 就一直持有对已销毁的 activity 实例的引用总结尽管使用 Java 的双括号初始化看起来很"炫酷",但它会无故地额外创建类,可能会导致内存泄漏。

因此避免在 Java 中使用双括号初始化你可以用下面这种更安全的方式来解决这个问题:Map metadata = new HashMap<>();metadata.put(

"ticketId", ticketId);bugsnagClient.leaveBreadcrumb("Saving Ticket", metadata, LOG);或者利用 Collections.singletonMap()

进一步简化代码:Map metadata = singletonMap("ticketId", ticketId);bugsnagClient.leaveBreadcrumb(

"Saving Ticket", metadata, LOG);或者,直接将文件转换为 Kotlin。你是否在使用 Java 时遇到过内存泄漏的问题?

亲爱的读者们,感谢您花时间阅读本文。如果您对本文有任何疑问或建议,请随时联系我。我非常乐意与您交流。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

河南中青旅行社综合资讯 奇遇综合资讯 盛世蓟州综合资讯 综合资讯 游戏百科综合资讯 新闻67433