廢話不說,直接上乾貨。
1:程式碼的方式
程式碼中使用Debug的getMemoryInfo(Debug.MemoryInfo memoryInfo)或ActivityManager的MemoryInfo[] getProcessMemoryInfo(int[] pids)
ActivityManager mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
Debug.MemoryInfo[] memoryInfoArray = mAm.getProcessMemoryInfo(pids);
複製程式碼
MemoryInfo物件中可以看看詳細的記憶體資訊,欄位如下: 目前很多記憶體測試工具都是採用這種方式,如哆啦A夢、GT等。這種方式簡單方便
2:dumpsys 命令
adb shell dumpsys meminfo package_name|pid
複製程式碼
一般關注關PSS列的這幾個欄位:
- Native Heap:JNI層的記憶體
- Dalvik Heap:Java層的記憶體分配情況
- Total PSS :應用真正佔據的記憶體大小
一般公司測試都是會採用這種方式獲取記憶體資料的
3:smaps檔案
Linux在2.6版本之後有一個proc偽檔案,在它下面記錄各種資訊,其中在proc/pid/smaps記錄某個pid的內,smaps記錄記憶體詳細的使用情況。使用下面的命令可以讀檔案的值
cat /proc/pid/smaps
複製程式碼
檔案格式如下:
檔案欄位意義:- 400ca000-400cb000:本段虛擬記憶體的地址範圍
- r-xp :檔案許可權,r(讀)、w(寫)、x(執行)、p表示私有,s代表共享,如果不具有哪項許可權用"-"代替
- 00000000 :對映檔案的偏移量
- b3:11 :檔案裝置號
- 1345 :被對映到虛擬記憶體檔案的映索節點
- /system/lib/libplddbgutil.so:檔名稱
- Size:相應虛擬地址空間的大小
- RSS: 正在使用的實體記憶體的大小
- Shared_Clean: Rss中和其他程式共享的未使用頁數
- Shared_Dirty: Rss和其他程式共享已經使用的頁數
- Private_Clean: Rss私有區域未使用的頁數
- Private_Dirty: Rss私有區域已經使用的頁數
smaps檔案一般有啥作用呢,譬如我們通過dumpsys meminfo 獲取記憶體時,發現某一項記憶體資料異常,想弄清楚資料都是有哪些檔案產生,我們就可以通過讀取smaps詳細排查。
4:原始碼分析
我們執行dumpsys memInfo 命令後的程式碼執行流程是怎樣的呢,dumpsys命令會根據傳進來的引數通過函式checkService來找到具體的service, 然後執行該service的dump方法,最總達到dump service的目的。
那我們傳進入的memInfo對應的是哪個service呢,這個我們需要檢視ActivityManagerService.Java類,搜尋一下memInfo,最後我們發現它對應的service是MemBinder。
我們點選MemBinder類看類內部的實現
方法內部首先會進行android.Manifest.permission.DUMP許可權檢查,最終呼叫 dumpApplicationMemoryUsage方法,我們再看dumpApplicationMemoryUsage方法做了哪些操作
這個方法內容比較多,這裡我們只顯示了它最終調的地方,我們最終發現最後會呼叫android.os.Debug.java類。
其實會呼叫android.os.Debug.java類呼叫了大量的Native方法實現的,android.os.Debug.java對應的native檔案是android_os_Debug.cpp,上面的Debug.getMemoryInfo(pid, mi)實際上最後呼叫了 android_os_Debug.cpp的android_os_Debug_getDirtyPagesPid方法
android_os_Debug_getDirtyPagesPid方法呼叫了load_maps(int pid, stats_t* stats)方法
load_maps中是通過讀取smaps檔案內容,到此dumpsys memInfo的實現就非常清晰了。
第一種程式碼的實現方式,其實最後走的流程和上面的是一樣的。
android_os_Debug.cpp地址: android_os_Debug.cpp檔案
5:記憶體測試關注的點
1.記憶體是否持續上漲:進入某項功能記憶體增長,退出功能時記憶體是否下降
2.是否頻繁的GC:頻繁的gc可能是因為你的程式記憶體碎片導致的,這也是需要優化的一個方向
3.記憶體的峰值是否在單個應用允許的最大值之內