Android - 看似內存泄漏,實則不是,記一次內存泄漏的案例分析


  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工具,來分析,得到與此對象相關的引用鏈如下:

  

  

  由上圖可知未被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來回收。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM