一、Android內(nèi)存分析基礎(chǔ)
1、查看進程分配的最大內(nèi)存
每個App進程可以分配到的最大內(nèi)存是有限的,當然不同型號的手機,為每個App進程分配的最大內(nèi)存有可能也不一樣
可以通過以下命令查看APP給每個進程分配的最大堆內(nèi)存:
adb shell getprop | grep dalvik.vm.heapsize

2、查看進程的內(nèi)存使用情況
adb shell dumpsys meminfo [包名]

Pss: 該進程獨占的內(nèi)存+與其他進程共享的內(nèi)存(按比例分配,比如與其他3個進程共享9K內(nèi)存,則這部分為3K)
Privete Dirty:該進程獨享內(nèi)存
Heap Size:分配的內(nèi)存
Heap Alloc:已使用的內(nèi)存
Heap Free:空閑內(nèi)存
二、使用Android Profiler查看內(nèi)存
在AndroidStudio中通過 View -> Tool Windows -> Android Profiler 可以找到Android Profiler窗口。

打開Android Profiler窗口,如下所示:

· 進程占用總內(nèi)存
· javaHeap:這部分內(nèi)存大小是有限制的,溢出則會OOM,這部分內(nèi)存也是我們分析優(yōu)化的重點
· NativeHeap:native層的 so 中調(diào)用malloc或new創(chuàng)建的內(nèi)存,對于單個進程來說大小沒有限制,所以可以利用在native層分配內(nèi)存來緩解javaHeap的壓力(比如2.3.3之前Android Bitmap的內(nèi)存分配就是在native層,之后移到javaHeap, 8.0又回到native)
· Graphics:這部分一般游戲app中用的較多,OpenGL和SurfaceFlinger相關(guān)的內(nèi)存,若沒有直接調(diào)用到OpenGL,則一般不會涉及到這塊內(nèi)存
· Stack:java虛擬機棧內(nèi)存
· Code:代碼,主要是dex以及so等占用的內(nèi)存
· Others:就是others
所以我們可以看到事實上我們可以優(yōu)化的點有:JavaHeap、NativeHeap、Stack、Code所占用的內(nèi)存
三、 使用Android Studio和MAT進行內(nèi)存泄露分析
1、Android的內(nèi)存泄漏分析工具常用有Android Studio和基于eclipse的MAT(Memory Analyzer Tool)。通過兩者配合,可以發(fā)揮出奇妙的效果。Android Studio能夠快速定位內(nèi)存泄漏的Activity,MAT能根據(jù)已知的Activity快速找出內(nèi)存泄漏的根源。
第一步:強制GC,生成Java Heap文件
我們都知道Java有一個非常強大的垃圾回收機制,會幫我回收無引用的對象,這些無引用的對象不在我們內(nèi)存泄漏分析的范疇,Android Studio有一個Android Monitors幫助我們進行強制GC,獲取Java Heap文件。
強制GC:點擊Force Garbage Collection(1)按鈕,建議點擊后等待幾秒后再次點擊,嘗試多次,讓GC更加充分。然后點擊Dump Java Heap(2)按鈕,然后等到一段時間,便會生成.hrof文件。

生成的Java Heap文件會在新建窗口打開。我們可以通過點擊把.hprof文件保存到本地。

第二步:分析內(nèi)存泄漏的Activity
然后可以把上一次生成的.hprof拖拽到Android Studio中,然后點擊右邊的一個Analyzer Tasks 按鈕,如下所示:

找到可能產(chǎn)生泄露的Activity,點擊綠色按鈕,最后便可以查看泄露的引用鏈,如下所示:
個人感覺AndroidStudio查看泄露不是很準確,如果想準確知道泄露的引用鏈的話,可以結(jié)合MAT來分析。
第三步:轉(zhuǎn)換成標準的hprof文件
前提:安裝elipse和MAT(https://blog.csdn.net/u010335298/article/details/52233689)
1、打開cmd窗口,然后進入到Android sdk tools 目錄中
2、執(zhí)行hprof-conv befor.hprof after.hprof 命令
3、把標準的hprof文件拖入到elipse中

第四步:查看內(nèi)存泄露的引用鏈


右擊搜索出來的類名,選擇Merge Shortest Paths to GC Roots的exclude all phantom/weak/soft etc. references,來到這一步,就可以看到內(nèi)存泄漏的原因,我們就需要根據(jù)內(nèi)存泄漏的信息集合我們的代碼去分析原因。
代碼如下
public class AppSetting {
private static AppSetting sInstance;
private static Context mContext;//傳的是Activity
private AppSetting(Context context) {
mContext = context;
}
public static AppSetting getInstance(Context context) {
if (sInstance == null) {
sInstance = new AppSetting(context);
}
return sInstance;
}
public void doSomeThing(){
}
}
四、使用LeakCanary進行內(nèi)存泄露檢測
只要集成程序自動幫忙抓取內(nèi)存泄露,gitHub地址如下: