APP中常常會存在記憶體洩漏的問題,一個簡單的測試方法是,多次進入和退出同一頁面(Activity),使用adb shell中的dumpsys meminfo com.android.settings | grep "Activities"來檢視Activity的數量(以com.android.settings為例)。
如果随着多次進入和退出,Activity的數量一緻在增長,沒有下降,那麼便很大有可能是記憶體洩漏的問題。當然有可能是GC還沒有回收的緣故,如果再顯示地對調用GC回收(DDMS工具的Cause GC按鈕),如果Acitivity的數量仍然沒有降低,那麼機率就更大了。需要從代碼層面進一步分析。
今天遇到的例子就是,通過上述方法,看似遇到了記憶體洩漏,其實不是。
關鍵點:通過MAT工具和代碼分析,未回收的對象被system_process程序引用,顯示調用system_process GC即可解決問題,不屬于記憶體洩漏。
案例簡介:在原生Android Open Source Project的Settings APP代碼中,有一個Fragment類叫AccountPreferenceBase,運作在程序com.android.settings中,通過以上方法,發現這個類可能存在記憶體洩漏,于是在重制問題後,借助MAT工具,來分析,得到與此對象相關的引用鍊如下:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5yNzMjN0MTMxEjMtYTOygDM4QTNxkTM4AjNxAjMtETM4YTN18CX4AjNxAjMvwVMxgjN1UzLcd2bsJ2Lc12bj5ycn9Gbi52YuUTMwIzcldWYtl2Lc9CX6MHc0RHaiojIsJye.png)
由上圖可知未被GC回收的AccountPreferenceBase與ContentResolver有關。通過代碼分析,在AccountPreferenceBase中,相關的代碼是如下,
進一步分析,在onResume時,調用addStatusChangeListener時,内部會調用RemoteCallbackList的register方法(将callback的binder對象push進一個ArrayMap)。如果不再頁面退出時,及時從ArrayMap中delete掉此binder對象,就會有記憶體洩漏的問題。但是我們在onPause中發現,其實已經調用了removeStatusChangeListener,其内部就會調用unregister方法,從ArrayMap中delete掉正确的binder對象。是以代碼的寫法沒有問題。
那是什麼原因導緻GC沒有回收我們的Activity呢?
原因就是,此ArrayMap是在system_process程序中,并非在com.android.settings的程序中,delete之後,如果執行一次GC(或者我們顯示地對system_process調用一次GC),那麼對象就會被回收。引用的settings程序中的Activity也會被回收釋放。
是以在此案例中,記憶體洩漏不存在。
是以在遇到記憶體洩露的情況時,還是需要根據代碼來具體分析,GC回收的時機不确定,可通過顯示地調用GC來回收對象,排除某些記憶體洩露的可能。當然跨程序時,要調用正确程序的GC來回收。