引言
前文內存分析工具集中介紹了一系列的內存分析工具及其基本使用, 諸如Memory Monitor, HPROF Viewer, MAT等等. 實際上了解了工具的使用, 我們就已經掌握了如何分析內存問題了.
為了能對工具的使用更加深入, 本篇將一個代碼片段為例, 從時序的角度講解下如何使用這些工具來分析一個內存泄露.
系列文:
1.GC那些事兒
2.Android的內存管理
3.內存分析工具
4.內存泄露實例分析
1, 例子
假設有一個單例的ListenerManager, 可以add / remove Listener, 有一個Activity, 實現了該listener, 且這個Activity中持有大對象BigObject, BigObject中包含一個大的字符串數組和一個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的自帶工具來分析
根據前文的工具介紹, Android Studio自帶了Memory Monitor, HPROF Viewer & Analyzer來分析內存使用及內存問題.
2.1 查看Memory使用, 并導出hprof文件
啟動我們要檢測的Activity(MemoryLeakActivity), 然后退出, 在monitor中查看內存變化:
2.2 在HPROF Viewer界面, 開始分析
第一步
點擊"Analyzer Tasks"視圖中的啟動按鈕, 啟動分析
第二步
查看"Analysis Result"中的分析結果, 點擊"Leaked Activityes"中的具體實例, 該實例的引用關系將會展示在"Reference Tree"視圖中.
第三步
根據"Reference Tree"視圖中的引用關系找到是誰讓這個leak的activity活著的, 也就是誰Dominate這個activity對象.
此例中, 比較簡單, 可以很清晰看到是ListenerManager的靜態單例sInstance最終支配了MemoryLeakActivity. sIntance連接到GC Roots, 故而導致MemoryLeakActivity GC Roots可達, 無法被回收.
2.3 使用Heap Viewer查看內存消耗
上述步驟, 可以讓我們快速定位可能的內存泄露. 當然, 內存問題, 除了內存泄露, 還有內存消耗過大. 我們可以在Heap Viewer中查看分析內存的消耗點, 如下:
3, MAT讓我們看的更多
就單純的分析Android App的內存使用和內存泄露來說, 個人覺得Android Studio自帶的工具已經足夠好了, 而且再持續變得更好, 也更便于Android的開發人員去理解. 故而其實一開始在Android性能分析工具一文中, 我就沒有詳細去提MAT. 相對與Android Studio中的Memory Monitor, HPROF工具來說, MAT的使用顯得更加生澀, 難以理解.
關于MAT的幫助文檔, 個人翻譯了一份, 需要的同學戳這里.
當然, 如果我們想對內存的使用相關知識了解得更多, 還是有必要了解下MAT的...
下面我們以幾個角度來了解下MAT的基本使用:
再次:
Android Studio導出的hprof文件需要轉換下才可以在MAT中使用.
$ hprof-conv com.anly.samples_2016.10.31_15.07.hprof mat.hprof
3.1 Histogram視圖定位內存消耗
MAT中很多視圖的第一行, 都可以輸入正則, 來匹配我們關注的對象實例.
3.2 Dominate Tree視圖查看支配關系
3.3 使用OQL查詢相關對象
對于Android App開發來說, 大部分的內存問題都跟四大組件, 尤其是Activity相關, 故而我們會想查出所有Activity實例的內存占用情況, 可以使用OQL來查詢:
具體OQL語法看這里.
3.4 GC路徑定位問題
上面幾個視圖都可以讓我們很快速的找到內存的消耗點, 接下來我們要分析的就是為何這些個大對象沒有被回收.
根據第一彈:GC那些事兒所言, 對象沒有被回收是因為他有到GC Roots的可達路徑. 那么我們就來分析下這條路徑(Path to GC Roots), 看看是誰在這條路中"搭橋".
如下, 進入該對象的"path2gc"視圖:

同樣, 與HPROF Analyzer異曲同工, 找出了是ListenerManager的靜態實例導致了MemoryLeakActivity無法回收.
4, LeakCanary讓內存泄露無處可藏
大道至簡, 程序員都應該"懶", 故而我們都希望有更方便快捷的方式讓我們發現內存泄露. 偉大的square發揮了這一優良傳統, 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 操作要檢測的界面, 查看結果
當發生可疑內存泄露時, 會在桌面生成一個"Leaks"的圖標, 點擊進去可以看到內存泄露的疑點報告:
可以看到, 結果與前二者的分析結果"驚人"一致, 不一致就出事兒了, :)
足夠方便且直觀吧, 趕快用起來吧.
當然, 內存問題不僅僅是內存泄露, 還有內存占用過多等, 這時我們就需要借助前兩種工具了.
結語
綜上, 建議LeakCanary集成作為App的必選項, 大多數情況下我們可以用LeakCanary結合Android Studio自帶的工具分析內存問題.
如果有精力, 還是建議深入了解下MAT, 能讓我們更深入了解GC的機制, 相關概念, MAT還有很多更高端的功能值得我們探索, 例如Heap比較等.
其實, 內存問題的分析, 無外乎分析對象的內存占用(Retained Size), 找出Retained Size大的對象, 找到其直接支配(Immediate Dominator), 跟蹤其GC可達路徑(Path to GC Roots), 從而找到是誰讓這個大對象活著. 找到問題癥結, 對癥下藥.