內(nèi)存泄露實(shí)例分析 -- Android內(nèi)存優(yōu)化第四彈

cover

引言

前文內(nèi)存分析工具集中介紹了一系列的內(nèi)存分析工具及其基本使用, 諸如Memory Monitor, HPROF Viewer, MAT等等. 實(shí)際上了解了工具的使用, 我們就已經(jīng)掌握了如何分析內(nèi)存問(wèn)題了.

為了能對(duì)工具的使用更加深入, 本篇將一個(gè)代碼片段為例, 從時(shí)序的角度講解下如何使用這些工具來(lái)分析一個(gè)內(nèi)存泄露.

系列文:
1.GC那些事兒
2.Android的內(nèi)存管理
3.內(nèi)存分析工具
4.內(nèi)存泄露實(shí)例分析

1, 例子

假設(shè)有一個(gè)單例的ListenerManager, 可以add / remove Listener, 有一個(gè)Activity, 實(shí)現(xiàn)了該listener, 且這個(gè)Activity中持有大對(duì)象BigObject, BigObject中包含一個(gè)大的字符串?dāng)?shù)組和一個(gè)Bitmap List.

代碼片段如下:

ListenerManager

public class ListenerManager {

    private static ListenerManager sInstance;
    private ListenerManager() {}

    private List<SampleListener> listeners = new ArrayList<>();

    public static ListenerManager getInstance() {
        if (sInstance == null) {
            sInstance = new ListenerManager();
        }

        return sInstance;
    }

    public void addListener(SampleListener listener) {
        listeners.add(listener);
    }

    public void removeListener(SampleListener listener) {
        listeners.remove(listener);
    }
}

MemoryLeakActivity

public class MemoryLeakActivity extends AppCompatActivity implements SampleListener {

    private BigObject mBigObject = new BigObject();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_memory_leak);

        ListenerManager.getInstance().addListener(this);
    }

    @Override
    public void doSomething() {

    }
}

具體代碼參看Github.

2, 使用Android Studio的自帶工具來(lái)分析

根據(jù)前文的工具介紹, Android Studio自帶了Memory Monitor, HPROF Viewer & Analyzer來(lái)分析內(nèi)存使用及內(nèi)存問(wèn)題.

2.1 查看Memory使用, 并導(dǎo)出hprof文件

啟動(dòng)我們要檢測(cè)的Activity(MemoryLeakActivity), 然后退出, 在monitor中查看內(nèi)存變化:


2.2 在HPROF Viewer界面, 開(kāi)始分析

第一步

點(diǎn)擊"Analyzer Tasks"視圖中的啟動(dòng)按鈕, 啟動(dòng)分析

第二步

查看"Analysis Result"中的分析結(jié)果, 點(diǎn)擊"Leaked Activityes"中的具體實(shí)例, 該實(shí)例的引用關(guān)系將會(huì)展示在"Reference Tree"視圖中.

第三步

根據(jù)"Reference Tree"視圖中的引用關(guān)系找到是誰(shuí)讓這個(gè)leak的activity活著的, 也就是誰(shuí)Dominate這個(gè)activity對(duì)象.

此例中, 比較簡(jiǎn)單, 可以很清晰看到是ListenerManager的靜態(tài)單例sInstance最終支配了MemoryLeakActivity. sIntance連接到GC Roots, 故而導(dǎo)致MemoryLeakActivity GC Roots可達(dá), 無(wú)法被回收.

關(guān)于Dominate, GC Roots, GC Roots可達(dá), 活對(duì)象等概念, 請(qǐng)結(jié)合前兩篇的理論文1, 2觀看.

2.3 使用Heap Viewer查看內(nèi)存消耗

上述步驟, 可以讓我們快速定位可能的內(nèi)存泄露. 當(dāng)然, 內(nèi)存問(wèn)題, 除了內(nèi)存泄露, 還有內(nèi)存消耗過(guò)大. 我們可以在Heap Viewer中查看分析內(nèi)存的消耗點(diǎn), 如下:

3, MAT讓我們看的更多

就單純的分析Android App的內(nèi)存使用和內(nèi)存泄露來(lái)說(shuō), 個(gè)人覺(jué)得Android Studio自帶的工具已經(jīng)足夠好了, 而且再持續(xù)變得更好, 也更便于Android的開(kāi)發(fā)人員去理解. 故而其實(shí)一開(kāi)始在Android性能分析工具一文中, 我就沒(méi)有詳細(xì)去提MAT. 相對(duì)與Android Studio中的Memory Monitor, HPROF工具來(lái)說(shuō), MAT的使用顯得更加生澀, 難以理解.

關(guān)于MAT的幫助文檔, 個(gè)人翻譯了一份, 需要的同學(xué)戳這里.

當(dāng)然, 如果我們想對(duì)內(nèi)存的使用相關(guān)知識(shí)了解得更多, 還是有必要了解下MAT的...
下面我們以幾個(gè)角度來(lái)了解下MAT的基本使用:

再次:
Android Studio導(dǎo)出的hprof文件需要轉(zhuǎn)換下才可以在MAT中使用.

$ hprof-conv com.anly.samples_2016.10.31_15.07.hprof mat.hprof

3.1 Histogram視圖定位內(nèi)存消耗

MAT中很多視圖的第一行, 都可以輸入正則, 來(lái)匹配我們關(guān)注的對(duì)象實(shí)例.

3.2 Dominate Tree視圖查看支配關(guān)系

3.3 使用OQL查詢(xún)相關(guān)對(duì)象

對(duì)于Android App開(kāi)發(fā)來(lái)說(shuō), 大部分的內(nèi)存問(wèn)題都跟四大組件, 尤其是Activity相關(guān), 故而我們會(huì)想查出所有Activity實(shí)例的內(nèi)存占用情況, 可以使用OQL來(lái)查詢(xún):


具體OQL語(yǔ)法看這里.

3.4 GC路徑定位問(wèn)題

上面幾個(gè)視圖都可以讓我們很快速的找到內(nèi)存的消耗點(diǎn), 接下來(lái)我們要分析的就是為何這些個(gè)大對(duì)象沒(méi)有被回收.

根據(jù)第一彈:GC那些事兒所言, 對(duì)象沒(méi)有被回收是因?yàn)樗械紾C Roots的可達(dá)路徑. 那么我們就來(lái)分析下這條路徑(Path to GC Roots), 看看是誰(shuí)在這條路中"搭橋".

如下, 進(jìn)入該對(duì)象的"path2gc"視圖:
![Slice 1](http://oat9lzupi.bkt.clouddn.com/Slice 1.jpg)

同樣, 與HPROF Analyzer異曲同工, 找出了是ListenerManager的靜態(tài)實(shí)例導(dǎo)致了MemoryLeakActivity無(wú)法回收.

4, LeakCanary讓內(nèi)存泄露無(wú)處可藏

大道至簡(jiǎn), 程序員都應(yīng)該"懶", 故而我們都希望有更方便快捷的方式讓我們發(fā)現(xiàn)內(nèi)存泄露. 偉大的square發(fā)揮了這一優(yōu)良傳統(tǒng), LeakCanary面世.

4.1 加入LeakCanary

app的build.gradle中加入:

dependencies {
   debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
   releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
   testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
 }

Application中加入:

public class SampleApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        LeakCanary.install(this);
    }
}

4.2 操作要檢測(cè)的界面, 查看結(jié)果

當(dāng)發(fā)生可疑內(nèi)存泄露時(shí), 會(huì)在桌面生成一個(gè)"Leaks"的圖標(biāo), 點(diǎn)擊進(jìn)去可以看到內(nèi)存泄露的疑點(diǎn)報(bào)告:


可以看到, 結(jié)果與前二者的分析結(jié)果"驚人"一致, 不一致就出事兒了, :)

足夠方便且直觀吧, 趕快用起來(lái)吧.
當(dāng)然, 內(nèi)存問(wèn)題不僅僅是內(nèi)存泄露, 還有內(nèi)存占用過(guò)多等, 這時(shí)我們就需要借助前兩種工具了.

結(jié)語(yǔ)

綜上, 建議LeakCanary集成作為App的必選項(xiàng), 大多數(shù)情況下我們可以用LeakCanary結(jié)合Android Studio自帶的工具分析內(nèi)存問(wèn)題.

如果有精力, 還是建議深入了解下MAT, 能讓我們更深入了解GC的機(jī)制, 相關(guān)概念, MAT還有很多更高端的功能值得我們探索, 例如Heap比較等.

其實(shí), 內(nèi)存問(wèn)題的分析, 無(wú)外乎分析對(duì)象的內(nèi)存占用(Retained Size), 找出Retained Size大的對(duì)象, 找到其直接支配(Immediate Dominator), 跟蹤其GC可達(dá)路徑(Path to GC Roots), 從而找到是誰(shuí)讓這個(gè)大對(duì)象活著. 找到問(wèn)題癥結(jié), 對(duì)癥下藥.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,781評(píng)論 25 709
  • 內(nèi)存管理的目的就是讓我們?cè)陂_(kāi)發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問(wèn)題。內(nèi)存泄漏大家都不陌生了,簡(jiǎn)單粗俗的講,...
    宇宙只有巴掌大閱讀 2,481評(píng)論 0 12
  • 四月的計(jì)劃完成的如何了?五月的計(jì)劃里,四月的計(jì)劃是否重復(fù)著? 四月的你,日子過(guò)得好么?五月的你,準(zhǔn)備怎樣活? 上個(gè)...
    MsCactus閱讀 314評(píng)論 0 2
  • Q:小丸子張晗你好!聽(tīng)說(shuō)你是在職事業(yè)單位工作,同時(shí)業(yè)余時(shí)間兼職做微商,平時(shí)還要帶十個(gè)月大的寶寶,請(qǐng)問(wèn)你為什么要參加...
    奔跑的丸子啊閱讀 524評(píng)論 9 52
  • 獨(dú)立歡騰舒駿馬,野鄉(xiāng)縱橫自乾坤。 平生不做安寧事,初老猶癡寨里春。 千里奮蹄戲千里,風(fēng)云變換淡風(fēng)云。 世間俗事逍遙...
    溪竹青閱讀 886評(píng)論 0 7

友情鏈接更多精彩內(nèi)容