Android - 看似内存泄漏,实则不是,记一次内存泄漏的案例分析

  APP中经常会存在内存泄漏的问题,一个简单的测试方法是,屡次进入和退出同一页面(Activity),使用adb shell中的dumpsys meminfo com.android.settings | grep "Activities"来查看Activity的数量(以com.android.settings为例)。android

  若是随着屡次进入和退出,Activity的数量一致在增加,没有降低,那么便很大有多是内存泄漏的问题。固然有多是GC尚未回收的缘故,若是再显示地对调用GC回收(DDMS工具的Cause GC按钮),若是Acitivity的数量仍然没有下降,那么几率就更大了。须要从代码层面进一步分析。shell

  

  今天遇到的例子就是,经过上述方法,看似遇到了内存泄漏,其实不是工具

  关键点经过MAT工具和代码分析,未回收的对象被system_process进程引用,显示调用system_process GC便可解决问题,不属于内存泄漏。测试

  

  案例简介:在原生Android Open Source Project的Settings APP代码中,有一个Fragment类叫AccountPreferenceBase,运行在进程com.android.settings中,经过以上方法,发现这个类可能存在内存泄漏,因而在重现问题后,借助MAT工具,来分析,获得与此对象相关的引用链以下:spa

  

  

  由上图可知未被GC回收的AccountPreferenceBase与ContentResolver有关。经过代码分析,在AccountPreferenceBase中,相关的代码是以下,对象

  

  

  进一步分析,在onResume时,调用addStatusChangeListener时,内部会调用RemoteCallbackList的register方法(将callback的binder对象push进一个ArrayMap)。若是再也不页面退出时,及时从ArrayMap中delete掉此binder对象,就会有内存泄漏的问题。可是咱们在onPause中发现,其实已经调用了removeStatusChangeListener,其内部就会调用unregister方法,从ArrayMap中delete掉正确的binder对象。因此代码的写法没有问题。blog

   

  那是什么缘由致使GC没有回收咱们的Activity呢?进程

  缘由就是,此ArrayMap是在system_process进程中,并不是在com.android.settings的进程中,delete以后,若是执行一次GC(或者咱们显示地对system_process调用一次GC),那么对象就会被回收。引用的settings进程中的Activity也会被回收释放。内存

  

  因此在此案例中,内存泄漏不存在。ci

  所以在遇到内存泄露的状况时,仍是须要根据代码来具体分析,GC回收的时机不肯定,可经过显示地调用GC来回收对象,排除某些内存泄露的可能。固然跨进程时,要调用正确进程的GC来回收。

相关文章
相关标签/搜索