日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

【編者按】本文介紹了一個(gè)使用了 JAVA 的雙括號(hào)初始化語(yǔ)法導(dǎo)致內(nèi)存泄漏的案例。作者分析了泄漏的原因,提出了幾種解決的方法,并給出了代碼示例。

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

作者 | Pierre-Yves Ricau責(zé)編 | 明明如月

責(zé)編 | 夏萌

出品 | CSDN(ID:CSDNnews)

結(jié)論先行

避免像這樣,在 Java 中使用雙括號(hào)初始化:

newHashMap< String, String> {{ put( "key", value); }};

內(nèi)存泄漏追蹤

我最近正在 LeakCanary看到了以下內(nèi)存泄漏追蹤信息:

┬─── │ 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 instance Leaking: YES (Activity#mDestroyed istrue)

當(dāng)打開(kāi)一個(gè)內(nèi)存泄漏追蹤日志時(shí),我首先會(huì)看底部的對(duì)象,了解它的生命周期,這將幫助我理解內(nèi)存泄漏追蹤中的其他對(duì)象是否應(yīng)該有相同的生命周期。

在底部,我們看到:

???????╰→ com.example.MainActivityinstance Leaking: YES( Activity#mDestroyedistrue)

Activity已經(jīng)被銷毀,應(yīng)該已被垃圾回收器給回收掉了,但它仍駐留在內(nèi)存中。

此時(shí),我開(kāi)始在內(nèi)存泄漏追蹤日志中尋找已知類型,并嘗試弄清楚它們是否屬于同一個(gè)被銷毀的范圍(=> 正在泄漏)或更高的范圍(=> 沒(méi)有泄漏)。

在頂部,我們看到:

???????├─ com.bugsnag.android.Clientinstance │ Leaking: UNKNOWN

我們的 BugSnag客戶端是一個(gè)用于分析崩潰報(bào)告單例,由于每個(gè)應(yīng)用我們創(chuàng)建一個(gè)實(shí)例,所以它沒(méi)有泄漏。

???????├─ com.bugsnag.android.Clientinstance │ Leaking: NO

所以我們現(xiàn)在需要轉(zhuǎn)變焦點(diǎn),特別關(guān)注從最后一個(gè) Leaking: NO到第一個(gè) 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 instance Leaking: YES (Activity #mDestroyed is true)

BugSnag 客戶端保持了一個(gè)面包屑的環(huán)形緩沖區(qū)。這些應(yīng)該保留在內(nèi)存中,它們也沒(méi)有泄漏。

所以讓我們跳過(guò)上述內(nèi)容,從下面這里繼續(xù)分析:

???????├─ com.bugsnag.android.BreadcrumbInternalinstance │ Leaking: NO

我們只需要關(guān)注從最后一個(gè) Leaking: NO到第一個(gè)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 instance Leaking: YES (Activity #mDestroyed is true)
  • BreadcrumbInternal.metadata :內(nèi)存泄漏追蹤通過(guò)面包屑實(shí)現(xiàn)的元數(shù)據(jù)字段。

也就是說(shuō):記錄到 BugSnag 的面包屑之一有一個(gè)元數(shù)據(jù)映射,這是一個(gè) HashMap的匿名子類 ,它保留對(duì)外部類的引用,這個(gè)外部類就是被銷毀的 Activity 。

讓我們看看我們?cè)?nbsp;MainActivity中記錄面包屑的地方:

voidlogSavingTicket( StringticketId) { Map< String, Object> metadata = newHashMap< String, Object> {{ put( "ticketId", ticketId); }}; bugsnagClient.leaveBreadcrumb( "Saving Ticket", metadata, LOG); }

這段代碼利用了一個(gè)被稱為“雙括號(hào)初始化” 的有趣的 Java 代碼塊 。它允許你創(chuàng)建一個(gè) HashMap,并通過(guò)添加代碼到HashMap的匿名子類的構(gòu)造函數(shù)中同時(shí)初始化它。

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); }

實(shí)際上被編譯為:

???????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); }

結(jié)果,這個(gè) breadcrumb 就一直持有對(duì)已銷毀的 activity 實(shí)例的引用。

總結(jié)

盡管使用 Java 的雙括號(hào)初始化看起來(lái)很"炫酷",但它會(huì)無(wú)故地額外創(chuàng)建類,可能會(huì)導(dǎo)致內(nèi)存泄漏。因此避免在 Java 中使用雙括號(hào)初始化。

你可以用下面這種更安全的方式來(lái)解決這個(gè)問(wèn)題:

???????Map< String, Object> metadata = newHashMap<>; metadata.put( "ticketId", ticketId); bugsnagClient.leaveBreadcrumb( "Saving Ticket", metadata, LOG);

或者利用 Collections.singletonMap進(jìn)一步簡(jiǎn)化代碼:

???????Map< String, Object> metadata = singletonMap( "ticketId", ticketId); bugsnagClient.leaveBreadcrumb( "Saving Ticket", metadata, LOG);

或者,直接將文件轉(zhuǎn)換為 Kotlin。

你是否在使用 Java 時(shí)遇到過(guò)內(nèi)存泄漏的問(wèn)題?

分享到:
標(biāo)簽:Java
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定