一個APP內存泄露問題的解決過程

一、如何發現內存泄露了

1.打開android studio,運行APP,android studio底部欄選擇 “Android Monitor”的“Monitors”視圖

2.在Monitors界面的上部分,左邊下拉框選擇運行APP的手機或模擬器,右邊下拉框選擇要調試的APP進程。

3.在Monitors界面的中間部分重點關注“Memory”這一塊的內存值的變化。

? 當打開一個Activity后,已分配內存“Allocated”值會變大,再退出,按一下gc按鈕

此時Activity正常情況下應該會被回收,已分配內存值“Allocated”應該會恢復成打開之前的值。

4.生成hprof文件進行驗證與分析

點擊“Dump java Heap”生成hprof文件

大概5秒后,hprof文件會被自動生成,并自動顯示在代碼瀏覽區域

此視圖會顯示對象的類型與實例個數,我們可以按包名進行分類,這樣更方便查找自己定義的類

二、通過hprof文件分析內存泄露

用Package Tree View分類,能很快找到我們需要分析的Activity

(本APP的 launcher Activity點擊進去,第二級的Activity名為MainActivity,當按返回按鈕后,MainActivity正常情況下要被回收,我們正是分析MainActivity為什么發生內存泄露)

我在launcher Activity里點擊進MainActivity 4次并返回,通過上圖可以發現,回到launcher Activity后,MainActivty每次創建的Activity實例在返回后并沒有被回收,如果這樣重復操作很多次,程序肯定會因內存不足而崩潰。

點擊上圖的右上“Instance”區域里的某一個實例,在下方“Reference Tree”區域里會列出所有持有該實例的引用對象。


只靠人工分析hprof文件是否能找出內存泄露點?

??????? 本APP的MainActivity非常復雜,所有子界面都采用的是Fragment來進行切換,持有MainActivity引用的其它對象眾多,所以只靠人工這樣去看,很難發現問題所在。有人建議使用MAT工具打開hprof文件進行分析,本篇有更簡單的方法,即使用LeakCanary工具。

三、使用LeakCanary工具查找內存泄漏

1.進入“https://github.com/square/leakcanary”查看LeakCanary的最新版本與使用方法

最新版本是1.5.1,使用方法很簡單,只有兩步。

2.集成LeakCanary到APP中

第一步:build.gradle里添加依賴

debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'

第二步:在自定義Application中初始化

public class ExampleApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
LeakCanary.install(this);
// Normal app init code...
}
}

OK。

3.重新RUN APP,在模擬器中進行測試

?? 我進入MainActivity4次,做一些操作,最后每次都返回到launcher Activity中,不一會兒,模擬器標題欄收到4個通知圖標

下拉點擊某一個

4.進入details界面

上圖中MainActivity對象生成后,從下到上一直追述到 android.view.inputmethod.InputMethodManager$ControlledInputConnectionWrapper.mMainLooper,

同時我們可以查看logcat

顯示android.view.inputmethod.InputMethodManager$ControlledInputConnectionWrapper.mMainLooper為GC ROOT,正是因為MainActivtiy被GC ROOT所持有,所以它不能被收回,發生內存泄露。

5.分析leaks details界面

?????? 里面引用MainActivity實例的都是系統對象,而且引用鏈條顯示是直接的引用,換句話就是說,如果在MainActivity里有一個Fragment,Fragment里面的ImageView引用了MainActivity,那么此leaks details界面的引用鏈不會把Fragment實例顯示出來,只會顯示ImageView實例。

?????? 這樣又增加了分析的難度,但是仔細分析,我們發現中間有一個

?????? references android.support.v7.widget.AppCompatImageView.mContext

?說明是某個ImageView持有MainActivity引用沒有被釋放,而又有一條

????? references android.animation.ValueAnimator$AnimationHandler.mAnimations

說明很可能是ImageView執行了屬性動畫,導致了內存泄露。

???? 分析到此處,我們大概鎖定了內存泄露的點,由于代碼很多,去一個一個找有點麻煩,那么就在項目里用關鍵字“ValueAnimator”或“ObjectAnimator”進行全局搜索??

??? 終于在“CustomProgressDialog.java”查找到了動畫的使用,ivIcon剛好是一個ImageView實例

private void startPropertyAnim() {
// 第二個參數"rotation"表明要執行旋轉
// 0f -> 360f,從旋轉360度,也可以是負值,負值即為逆時針旋轉,正值是順時針旋轉。
ObjectAnimator anim = ObjectAnimator.ofFloat(ivIcon, "rotation", 0f, 360f);
anim.setRepeatCount(INFINITE);
// 動畫的持續時間,執行多久?
anim.setDuration(5000);
anim.setInterpolator(new LinearInterpolator());

// 正式開始啟動執行動畫
anim.start();
}

6.分析定位出的代碼,修正

CustomProgressDialog是一個自定義對話框,對話框顯示時,ImageView會執行旋轉動畫,但是對話框消失時,動畫并沒有被取消,導致了內存泄露。最后進行修正

private void startPropertyAnim() {
// 第二個參數"rotation"表明要執行旋轉
// 0f -> 360f,從旋轉360度,也可以是負值,負值即為逆時針旋轉,正值是順時針旋轉。
if (anim != null){
anim.cancel();
}

anim = ObjectAnimator.ofFloat(ivIcon, "rotation", 0f, 360f);
anim.setRepeatCount(INFINITE);
// 動畫的持續時間,執行多久?
anim.setDuration(5000);
anim.setInterpolator(new LinearInterpolator());

// 正式開始啟動執行動畫
anim.start();
}

@Override
public void dismiss() {
super.dismiss();
if (anim != null){
anim.cancel();
}
}

最后重新運行修改的程序,測試,發現內存泄露成功解決。

總結:

?????? ?當項目比較小,代碼量不多時,可能人工檢查一下,就能解決內存泄露的問題,但是當項目越來越龐大,代碼量非常大時,就需要利用工具來幫助進行檢查。就像上面這個問題,在沒有利用工具的情況下,本人花了大量時間看代碼都沒有檢查出來,而利用工具,很好的進行了定位,查找起來方向性非常明確,最后順利解決隱藏很深的一個內存泄露點。

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

推薦閱讀更多精彩內容