內(nèi)存泄露從入門到精通三部曲之常見原因與用戶實踐

常見原因

1.集合類

集合類如果僅僅有添加元素的方法,而沒有相應(yīng)的刪除機制,導(dǎo)致內(nèi)存被占用。如果這個集合類是全局性的變量 (比如類中的靜態(tài)屬性,全局性的 map 等即有靜態(tài)引用或 final 一直指向它),那么沒有相應(yīng)的刪除機制,很可能導(dǎo)致集合所占用的內(nèi)存只增不減。

2.單例模式

不正確使用單例模式是引起內(nèi)存泄露的一個常見問題,單例對象在被初始化后將在 JVM 的整個生命周期中存在(以靜態(tài)變量的方式),如果單例對象持有外部對象的引用,那么這個外部對象將不能被 JVM 正?;厥?,導(dǎo)致內(nèi)存泄露

3.Android 組件或特殊集合對象的使用

BraodcastReceiver,ContentObserver,F(xiàn)ileObserver,Cursor,Callback等在 Activity onDestroy 或者某類生命周期結(jié)束之后一定要 unregister 或者 close 掉,否則這個 Activity 類會被 system 強引用,不會被內(nèi)存回收。

不要直接對 Activity 進行直接引用作為成員變量,如果不得不這么做,請用 private WeakReference mActivity 來做,相同的,對于Service 等其他有自己聲明周期的對象來說,直接引用都需要謹慎考慮是否會存在內(nèi)存泄露的可能。

4. Handler

要知道,只要 Handler 發(fā)送的 Message 尚未被處理,則該 Message 及發(fā)送它的 Handler 對象將被線程 MessageQueue 一直持有。由于 Handler 屬于 TLS(Thread Local Storage) 變量, 生命周期和 Activity 是不一致的。因此這種實現(xiàn)方式一般很難保證跟 View 或者 Activity 的生命周期保持一致,故很容易導(dǎo)致無法正確釋放。如上所述,Handler 的使用要尤為小心,否則將很容易導(dǎo)致內(nèi)存泄露的發(fā)生。

5. Thread 內(nèi)存泄露

線程也是造成內(nèi)存泄露的一個重要的源頭。線程產(chǎn)生內(nèi)存泄露的主要原因在于線程生命周期的不可控。比如線程是 Activity 的內(nèi)部類,則線程對象中保存了 Activity 的一個引用,當線程的 run 函數(shù)耗時較長沒有結(jié)束時,線程對象是不會被銷毀的,因此它所引用的老的 Activity 也不會被銷毀,因此就出現(xiàn)了內(nèi)存泄露的問題。

6.一些不良代碼造成的內(nèi)存壓力

有些代碼并不造成內(nèi)存泄露,但是它們,或是對沒使用的內(nèi)存沒進行有效及時的釋放,或是沒有有效的利用已有的對象而是頻繁的申請新內(nèi)存。

6.1Bitmap 沒調(diào)用 recycle().

Bitmap 對象在不使用時,我們應(yīng)該先調(diào)用 recycle() 釋放內(nèi)存,然后才它設(shè)置為 null. 因為加載 Bitmap 對象的內(nèi)存空間,一部分是 java 的,一部分 C? ?的(因為 Bitmap 分配的底層是通過 JNI 調(diào)用的 )。 而這個 recyle() 就是針對 C 部分的內(nèi)存釋放。

6.2構(gòu)造 Adapter 時,沒有使用緩存的 convertView。

以業(yè)務(wù)測試過程中常見的部分內(nèi)存泄露實例來說明:

1. callback 只有 add 操作,沒有注銷 remove.

從引用關(guān)系可以看到當前 view 被 callback 引用,而 callback 被外部對象 sharkprotocolQueue 持有引用而導(dǎo)致泄漏。

2. 發(fā)送延時消息時,如果該消息未處理,在退出頁面后會導(dǎo)致該頁面無法回收。

Android 應(yīng)用啟動的時候會創(chuàng)建 UI 主線程的 Looper 對象,它存在于整個應(yīng)用的生命周期,用于處理消息隊列里的 Message。而這些 Message 會引用發(fā)送該消息的 Handler 對象。

那么問題來了,如果這些 Handler 是 Activity 的內(nèi)部類,那么當這些 Handler 的消息未處理完或者消息本身是延時消息的話,就會導(dǎo)致 Activity 退出后,從 Activity 到 Handler 到 Message 到 Looper 的引用鏈條一直存在,從而導(dǎo)致 Activity 的泄露!

3. 異步線程未完成前退出 Activity 等組件,可能會導(dǎo)致界面資源無法釋放。

這種情況是典型的線程對象導(dǎo)致的內(nèi)存泄露。原因也很簡單,線程 Thread 對象的 run 任務(wù)未執(zhí)行完之前,對象本身是不會釋放的。因此 Activity 等組件對象內(nèi)的線程對象成員如果有耗時任務(wù)(一般也都是耗時任務(wù)),就會導(dǎo)致一直持有組件本身的引用內(nèi)存泄露!

本文部分內(nèi)容和經(jīng)驗摘自網(wǎng)絡(luò),結(jié)合本次內(nèi)存泄露的排查總結(jié)予以歸納。

優(yōu)秀實踐

對 Activity 等組件的引用應(yīng)該控制在 Activity 的生命周期之內(nèi); 如果不能就考慮使用 getApplicationContext 或者 getApplication,以避免 Activity 被外部長生命周期的對象引用而泄露。

在代碼復(fù)審的時候關(guān)注長生命周期對象:全局性的集合、單例模式的使用、類的 static 變量等等。

盡量不要在靜態(tài)變量或者靜態(tài)內(nèi)部類中使用非靜態(tài)外部成員變量(包括context ),即使要使用,也要考慮適時把外部成員變量置空;也可以在內(nèi)部類中使用弱引用來引用外部類的變量。

Handler 的持有的引用對象最好使用弱引用,資源釋放時也可以清空 Handler 里面的消息。比如在 Activity onStop 或者 onDestroy 的時候,取消掉該 Handler 對象的 Message和 Runnable.

removeCallbacks(Runnable r) 或r emoveMessages(int what),或 removeCallbacksAndMessages(null)等。

線程 Runnable 執(zhí)行耗時操作,注意在頁面返回時及時取消或者把 Runnable 寫成靜態(tài)類。

a) 如果線程類是內(nèi)部類,改為靜態(tài)內(nèi)部類。

b) 線程內(nèi)如果需要引用外部類對象如 context,需要使用弱引用。

在 Java 的實現(xiàn)過程中,也要考慮其對象釋放,最好的方法是在不使用某對象時,顯式地將此對象賦空,如清空對圖片等資源有直接引用或者間接引用的數(shù)組(使用 array.clear() ; array = null),最好遵循誰創(chuàng)建誰釋放的原則。


http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=125&extra=page%3D2

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內(nèi)容