前文內存分析工具集中介紹了一系列的內存分析工具及其基本使用, 諸如Memory Monitor, HPROF Viewer, MAT等等. 實際上了解了工具的使用, 咱們就已經掌握瞭如何分析內存問題了.javascript
爲了能對工具的使用更加深刻, 本篇將一個代碼片斷爲例, 從時序的角度講解下如何使用這些工具來分析一個內存泄露.html
系列文:
1.GC那些事兒
2.Android的內存管理
3.內存分析工具
4.內存泄露實例分析java
假設有一個單例的ListenerManager, 能夠add / remove Listener, 有一個Activity, 實現了該listener, 且這個Activity中持有大對象BigObject, BigObject中包含一個大的字符串數組和一個Bitmap List.android
代碼片斷以下:git
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);
}
}複製代碼
MemoryLeakActivitygithub
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.數組
根據前文的工具介紹, Android Studio自帶了Memory Monitor, HPROF Viewer & Analyzer來分析內存使用及內存問題.android-studio
啓動咱們要檢測的Activity(MemoryLeakActivity), 而後退出, 在monitor中查看內存變化:
app
第一步
點擊"Analyzer Tasks"視圖中的啓動按鈕, 啓動分析
第二步
查看"Analysis Result"中的分析結果, 點擊"Leaked Activityes"中的具體實例, 該實例的引用關係將會展現在"Reference Tree"視圖中.
第三步
根據"Reference Tree"視圖中的引用關係找到是誰讓這個leak的activity活着的, 也就是誰Dominate這個activity對象.
此例中, 比較簡單, 能夠很清晰看到是ListenerManager的靜態單例sInstance最終支配了MemoryLeakActivity. sIntance鏈接到GC Roots, 故而致使MemoryLeakActivity GC Roots可達, 沒法被回收.
上述步驟, 可讓咱們快速定位可能的內存泄露. 固然, 內存問題, 除了內存泄露, 還有內存消耗過大. 咱們能夠在Heap Viewer中查看分析內存的消耗點, 以下:
就單純的分析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複製代碼
MAT中不少視圖的第一行, 均可以輸入正則, 來匹配咱們關注的對象實例.
對於Android App開發來講, 大部分的內存問題都跟四大組件, 尤爲是Activity相關, 故而咱們會想查出全部Activity實例的內存佔用狀況, 可使用OQL來查詢:
具體OQL語法看這裏.
上面幾個視圖均可以讓咱們很快速的找到內存的消耗點, 接下來咱們要分析的就是爲什麼這些個大對象沒有被回收.
根據第一彈:GC那些事兒所言, 對象沒有被回收是由於他有到GC Roots的可達路徑. 那麼咱們就來分析下這條路徑(Path to GC Roots), 看看是誰在這條路中"搭橋".
以下, 進入該對象的"path2gc"視圖:
一樣, 與HPROF Analyzer殊途同歸, 找出了是ListenerManager的靜態實例致使了MemoryLeakActivity沒法回收.
大道至簡, 程序員都應該"懶", 故而咱們都但願有更方便快捷的方式讓咱們發現內存泄露. 偉大的square發揮了這一優良傳統, 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);
}
}複製代碼
當發生可疑內存泄露時, 會在桌面生成一個"Leaks"的圖標, 點擊進去能夠看到內存泄露的疑點報告:
能夠看到, 結果與前兩者的分析結果"驚人"一致, 不一致就出事兒了, :)
足夠方便且直觀吧, 趕快用起來吧.
固然, 內存問題不單單是內存泄露, 還有內存佔用過多等, 這時咱們就須要藉助前兩種工具了.
綜上, 建議LeakCanary集成做爲App的必選項, 大多數狀況下咱們能夠用LeakCanary結合Android Studio自帶的工具分析內存問題.
若是有精力, 仍是建議深刻了解下MAT, 能讓咱們更深刻了解GC的機制, 相關概念, MAT還有不少更高端的功能值得咱們探索, 例如Heap比較等.
其實, 內存問題的分析, 無外乎分析對象的內存佔用(Retained Size), 找出Retained Size大的對象, 找到其直接支配(Immediate Dominator), 跟蹤其GC可達路徑(Path to GC Roots), 從而找到是誰讓這個大對象活着. 找到問題癥結, 對症下藥.