之前简单介绍了一下使用LeakCanary排查内存泄漏的使用,那么它为什么这么”智能”呢?为啥我们就加了一行代码它就可以监测内存泄漏了捏?这里就涉及LeakCanary的源码了。
源码分析 上面简单介绍了一下使用LeakCanary排查内存泄漏的使用,那么它为什么这么”智能”呢?为啥我们就加了一行代码它就可以监测内存泄漏了捏?这里就涉及LeakCanary的源码了。
在理解LeakCanary源码之前我们先来了解一下java虚拟机里的强引用,软引用,弱引用和虚引用。
强引用(Strong Reference):强引用在代码中普遍的存在,类似于“Object obj = new Object()”,只要某个对象有强引用与之关联,JVM则无法回收该对象,即使在内存不足的情况下,JVM宁愿抛出OOM错误,也不会回收这种对象。 
软引用(Soft Reference):软引用常常用来描述一些有用但是非必需的对象。对于软引用关联的对象,会在JVM内存不足时既OOM之前将这些对象列入回收范围,进行二次回收。如果这时回收还是没有足够的内存才会造成内存溢出异常。在JDK1.2之后,提供了SoftReference类来实现软引用。软引用一般用于网页的缓存图片的缓存等等比较耗时的操作,但是这些操作目前一般使用LruChche来实现,因此目前代码中很少见到SoftReference。 
弱引用(Weak Reference):被弱引用关联的对象只能生产到下一次垃圾收集发生之前。当垃圾收集器工作室,无论内存是否足够,都会回收弱引用关联的对象。可以使用WeakReference类来实现弱引用。 
虚引用(Phantom Reference):虚引用也称之为幽灵引用,或者幻影引用,它是最弱的一种引用关系,一个对象是否有虚引用的存在,完全不会对其生存时间构成影响。也无法通过虚引用来取得一个对象的实例,为一个对象设置为虚引用的唯一目的是希望这个对象被回收器回收时能收到一个系统通知,在JDK1.2之后,提供了Phantom Reference类来实现虚引用。 
 
而LeakCanary核心原理就是利用弱引用和引用队列ReferenceQueue来检测Activity/Fragment被销毁后是否被回收。如果弱引用的引用对象被垃圾回收器回收,虚拟机就会把这个弱引用加入到与之关联的引用队列中,我们就可以此特性来检查一个对象是否被垃圾回收器回收成功。我们将上述用代码简单实现就是下面这样
fun  main ()     var  obj: Any? = Object()     val  referenceQueue = ReferenceQueue<Any?>()     val  weakReference = WeakReference<Any?>(obj, referenceQueue)          var  ref = referenceQueue.poll()     println("gcBefore:${ref} " )          obj = null           System.gc()     Thread.sleep(2000 )          ref = referenceQueue.poll()     println("gcAfter:${ref} " ) } 
输出结果:
gcBefore:null gcAfter:java.lang.ref.WeakReference@60f82f98 Process finished with exit code 0 
恰好印证。那么我们知道了核心原理,是否可以实现一个简单版的LeakCanary捏?说干就干!
首先,我们需要封装一下我们的弱引用,因为往往我们观测的是多个对象,那么就有多个弱引用,所以我们需要给每个弱引用设置一个Key方便查找。
class  KeyWeakCanary <T > : WeakReference <T > {    private  var  key: String     constructor (referent: T, key: String) : super (referent) {         this .key = key     }     constructor (referent: T, queue: ReferenceQueue<in  T?>, key: String) : super (referent, queue) {         this .key = key     } 	     override  fun  toString ()          return  "KeyWeakReference(key=$key )"      } } 
弱引用简单封装了一下,那么还差个观察者,你要监测肯定需要一个观察者嘛。
class  Watcher  {         private  val  watchedReferences = mutableMapOf<String, KeyWeakReference<Any?>>()          private  val  retainedReferences = mutableMapOf<String, KeyWeakReference<Any?>>()          private  val  queue = ReferenceQueue<Any?>()     fun  watch (obj: Any ?)                   val  key = UUID.randomUUID().toString()         val  reference = KeyWeakReference(obj, queue, key)                  watchedReferences[key] = reference                  val  executor = Executors.newSingleThreadExecutor()         executor.execute {             Thread.sleep(1000 )             moveToRetain(key)         }     }          private  fun  moveToRetain (key: String )          var  ref: KeyWeakReference<Any?>? = null          do  {             queue.poll()?.also { ref = it as  KeyWeakReference<Any?> }                          ref?.key.let {                 watchedReferences.remove(it)                 retainedReferences.remove(it)             }             ref = null          } while  (ref != null )                           watchedReferences.remove(key)?.also {             retainedReferences[key] = it         }     }          fun  getReferences ()          return  retainedReferences     } } 
首先,我们创建了观察列表和留存列表,这就好比现在的疫情,从外省回来会被隔离(观察列表),如果观察你有新冠就将你运往隔离医院治疗(留存列表)。然后就是观察方法,传入观察对象,之后用java自带的UUID工具生成唯一的UUID给弱引用方便根据key查询弱引用,之后是弱引用引用对象,引用队列与弱引用关联,再把该弱引用记录进观察区,之后用线程池开启子线程进行内存监测。在内存检测方法里先从引用队列拿出一个引用对象,若为空,则表明它没有被回收,观察区也就不会将它移除,之后下面remove它的时候就不为null,它就会被转移进留存区。之后外界通过这个留存区取出内存泄漏的对象进行分析通知一系列操作。
然后在主线程测试一下
fun  main ()     var  obj:Any? = Object()     val  watcher = Watcher()     watcher.watch(obj)     obj = null      System.gc()     Thread.sleep(2000 )     watcher.getReferences().forEach { (key, reference) ->         println("key:$key ,$reference " )     } } 
将obj置为null此时调用GC obj将被回收,无输出。再将这行注释,模拟obj被引用的情况,此时GC无法将其回收,留存区有值,打印:
key:ca1485f4-929a-45f2-8977-483f05245f0d,KeyWeakReference(key=ca1485f4-929a-45f2-8977-483f05245f0d) Process finished with exit code 0 
证明我们这个简易版的(可以说很丑陋)内存泄漏检测工具是成功的。但实际上,LeakCanary的做法比我们这个更加细节,我们这个只能检测单个对象,而LeakCanary对Activity、Fragment、Service、RootView、Viewmodel都进行了生命周期监听,并且对泄漏对象的通报和分析都是在内部进行的。
我们现在来看看它的源码,在看他的源码之前我们先问几个问题
在LeakCanary1.0版本之前是需要在Application里面初始化的,2.0版本之后直接添加依赖就可以用了,那之后的版本它在哪初始化的捏? 
LeakCanary是怎么对Activity、fragment(view和fragment本身)、Service、RootView、Viewmodel进行生命周期的监听的捏? 
检测到泄漏之后是怎么处理的呢? 
 
相信带着这几个问题去看他的源码会更好理解。
首先看LeakCanary是在哪里初始化的?其实,在1.0版本之前,LeakCanary都是在自定义的Application里面初始化的,在2.0之后只需添加一行依赖即可。之前也提到过ContentProvider得onCreate()是在Application.onCreate前面执行的,不难想到LeakCanary就是这么许哦的。
为什么ContentProvider的onCreate方法是在Application前面执行的呢? 这就要看看ActivityThread的源码了,要知道他们两个的onCreate先后顺序肯定要先理清Application的创建流程,因为Application是伴随整个app的生命周期的。看到ActivityThread的main函数,我们看到
public  static  void  main (String[] args)  {    	...         Looper.prepareMainLooper(); 		...         ActivityThread  thread  =  new  ActivityThread ();         thread.attach(false , startSeq);     	...         Looper.loop();         throw  new  RuntimeException ("Main thread loop unexpectedly exited" );     } 
Application和整个app的生命周期那肯定也伴随着主线程的启动与消亡,那肯定跟thread.attach有关,跟进看一看
private  void  attach (boolean  system, long  startSeq)  {	sCurrentActivityThread = this ; 	mSystemThread = system; 	if  (!system) { 	    android.ddm.DdmHandleAppName.setAppName("<pre-initialized>" , 	                                            UserHandle.myUserId()); 	    RuntimeInit.setApplicationObject(mAppThread.asBinder()); 	     	    final  IActivityManager  mgr  =  ActivityManager.getService(); 	    try  { 	    	 	        mgr.attachApplication(mAppThread, startSeq); 	    } catch  (RemoteException ex) { 	        throw  ex.rethrowFromSystemServer(); 	    } 	    ... 	} 	... } 
这里获取AMS之后调用了attachApplication(mAppThread, startSeq),跟进看一下
public  void  attachApplication (IApplicationThread thread, long  startSeq)  {    if  (thread == null ) {         throw  new  SecurityException ("Invalid application interface" );     }     synchronized  (this ) {         int  callingPid  =  Binder.getCallingPid();         final  int  callingUid  =  Binder.getCallingUid();         final  long  origId  =  Binder.clearCallingIdentity();         attachApplicationLocked(thread, callingPid, callingUid, startSeq);         Binder.restoreCallingIdentity(origId);     } } private  boolean  attachApplicationLocked (@NonNull  IApplicationThread thread, int  pid, int  callingUid, long  startSeq)  {	... 	if  (app.isolatedEntryPoint != null ) { 	   	        	 	   thread.runIsolatedEntryPoint(app.isolatedEntryPoint, app.isolatedEntryPointArgs); 	} else  if  (instr2 != null ) { 	   thread.bindApplication(processName, appInfo, providerList, 	           instr2.mClass, 	           profilerInfo, instr2.mArguments, 	           instr2.mWatcher, 	           instr2.mUiAutomationConnection, testMode, 	           mBinderTransactionTrackingEnabled, enableTrackAllocation, 	           isRestrictedBackupMode || !normalMode, app.isPersistent(), 	           new  Configuration (app.getWindowProcessController().getConfiguration()), 	           app.compat, getCommonServicesLocked(app.isolated), 	           mCoreSettingsObserver.getCoreSettingsLocked(), 	           buildSerial, autofillOptions, contentCaptureOptions, 	           app.mDisabledCompatChanges); 	} else  { 	   thread.bindApplication(processName, appInfo, providerList, null , profilerInfo, 	           null , null , null , testMode, 	           mBinderTransactionTrackingEnabled, enableTrackAllocation, 	           isRestrictedBackupMode || !normalMode, app.isPersistent(), 	           new  Configuration (app.getWindowProcessController().getConfiguration()), 	           app.compat, getCommonServicesLocked(app.isolated), 	           mCoreSettingsObserver.getCoreSettingsLocked(), 	           buildSerial, autofillOptions, contentCaptureOptions, 	           app.mDisabledCompatChanges); 	} 	... } 
那么我们继续点进thread.bindApplication看看它做了什么
public  final  void  bindApplication (String processName, ApplicationInfo appInfo,                 ProviderInfoList providerList, ComponentName instrumentationName,                 ProfilerInfo profilerInfo, Bundle instrumentationArgs,                 IInstrumentationWatcher instrumentationWatcher,                 IUiAutomationConnection instrumentationUiConnection, int  debugMode,                 boolean  enableBinderTracking, boolean  trackAllocation,                 boolean  isRestrictedBackupMode, boolean  persistent, Configuration config,                 CompatibilityInfo compatInfo, Map services, Bundle coreSettings,                 String buildSerial, AutofillOptions autofillOptions,                 ContentCaptureOptions contentCaptureOptions, long [] disabledCompatChanges,                 SharedMemory serializedSystemFontMap)  {			...                         sendMessage(H.BIND_APPLICATION, data);         } 
找来找去发现没有关于application的函数了,但是我们可以看到最后一行调用了sendMessage函数,并传了H.BIND_APPLICATION这个参数,那么我们点进去看看它是否跟bindApplication有关捏?
void  sendMessage (int  what, Object obj)  {        sendMessage(what, obj, 0 , 0 , false );     } 
继续跟进
private  void  sendMessage (int  what, Object obj, int  arg1, int  arg2, boolean  async)  {        if  (DEBUG_MESSAGES) {             Slog.v(TAG,                     "SCHEDULE "  + what + " "  + mH.codeToString(what) + ": "  + arg1 + " / "  + obj);         }         Message  msg  =  Message.obtain();         msg.what = what;         msg.obj = obj;         msg.arg1 = arg1;         msg.arg2 = arg2;         if  (async) {             msg.setAsynchronous(true );         }         mH.sendMessage(msg);     } 
我们发现这里调用了mH的sendMessage方法,mH会不会就是Handler?跟进mH看一下
是H的对象,那H是否继承了Handler方法呢?点进去
class  H  extends  Handler  {    ...       	public  void  handleMessage (Message msg)  { 		if  (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: "  + codeToString(msg.what));             switch  (msg.what) {                 case  BIND_APPLICATION:                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication" );                     AppBindData  data  =  (AppBindData)msg.obj;                     handleBindApplication(data);                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);                     break ;                 case  EXIT_APPLICATION:                     if  (mInitialApplication != null ) {                         mInitialApplication.onTerminate();                     }                     Looper.myLooper().quit();                     break ;                 case  RECEIVER:                 ...                 }           ...          }     ...   } 
发现果然是这样。并且实现了handle Message方法,那上面我们sendMessage最终通过Handler肯定执行handle Message方法,并且根据我们传入的H.BIND_APPLICATION进入了第一个分支,继续跟进handleBindApplication(data);
@UnsupportedAppUsage     private  void  handleBindApplication (AppBindData data)  {         ...            		final  ContextImpl  appContext  =  ContextImpl.createAppContext(this , data.info); 	    ...         try  {                          app = data.info.makeApplication(data.restrictedBackupMode, null );             ...             if  (!data.restrictedBackupMode) {                 if  (!ArrayUtils.isEmpty(data.providers)) {                     installContentProviders(app, data.providers);                 }             }            ...             try  {                                  mInstrumentation.callApplicationOnCreate(app);             } catch  (Exception e) {                 if  (!mInstrumentation.onException(app, e)) {                     throw  new  RuntimeException (                       "Unable to create application "  + app.getClass().getName()                       + ": "  + e.toString(), e);                 }             } 		...     } 
可以看到,先创建appContext和application,具体创建过程这里就不细细分析了,这里我们只是简单看看ContentProvier和Application的onCreate执行顺序。然后是installContentProviders,那这里肯定就和ContentProvider有关了,等会再分析,先看下面的mInstrumentation.callApplicationOnCreate(app),我们跟进发现
public  void  callApplicationOnCreate (Application app)  {        app.onCreate();     } 
其实就是调用了application的onCreate方法,那我们再跟进前面的installContentProviders看看
private  void  installContentProviders (             Context context, List<ProviderInfo> providers)  {        final  ArrayList<ContentProviderHolder> results = new  ArrayList <>();         for  (ProviderInfo cpi : providers) {             if  (DEBUG_PROVIDER) {                 StringBuilder  buf  =  new  StringBuilder (128 );                 buf.append("Pub " );                 buf.append(cpi.authority);                 buf.append(": " );                 buf.append(cpi.name);                 Log.i(TAG, buf.toString());             }             ContentProviderHolder  cph  =  installProvider(context, null , cpi,                     false  , true  , true  );             if  (cph != null ) {                 cph.noReleaseNeeded = true ;                 results.add(cph);             }         }         try  {             ActivityManager.getService().publishContentProviders(                 getApplicationThread(), results);         } catch  (RemoteException ex) {             throw  ex.rethrowFromSystemServer();         }     } 
继续跟进installProvider
private  ContentProviderHolder installProvider (Context context,             ContentProviderHolder holder, ProviderInfo info,             boolean  noisy, boolean  noReleaseNeeded, boolean  stable)  {        ContentProvider  localProvider  =  null ;         IContentProvider provider;         if  (holder == null  || holder.provider == null ) {             ...             try  {                 final  java.lang.ClassLoader  cl  =  c.getClassLoader();                 LoadedApk  packageInfo  =  peekPackageInfo(ai.packageName, true );                 if  (packageInfo == null ) {                                          packageInfo = getSystemContext().mPackageInfo;                 }                 localProvider = packageInfo.getAppFactory()                         .instantiateProvider(cl, info.name);                 provider = localProvider.getIContentProvider();                 if  (provider == null ) {                     Slog.e(TAG, "Failed to instantiate class "  +                           info.name + " from sourceDir "  +                           info.applicationInfo.sourceDir);                     return  null ;                 }                 if  (DEBUG_PROVIDER) Slog.v(                     TAG, "Instantiating local provider "  + info.name);                                  localProvider.attachInfo(c, info);             } catch  (java.lang.Exception e) {                 if  (!mInstrumentation.onException(null , e)) {                     throw  new  RuntimeException (                             "Unable to get provider "  + info.name                             + ": "  + e.toString(), e);                 }                 return  null ;             }         } else  {             provider = holder.provider;             if  (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider "  + info.authority + ": "                      + info.name);         } 		...         return  retHolder;     } 
看到这句英文注释Need to create the correct context for this provider.需要为此提供者创建正确的上下文,那肯定就是他了,跟进localProvider.attachInfo(c, info)
private  void  attachInfo (Context context, ProviderInfo info, boolean  testing)  {        mNoPerms = testing;         mCallingAttributionSource = new  ThreadLocal <>();                  if  (mContext == null ) {             mContext = context;             if  (context != null  && mTransport != null ) {                 mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(                         Context.APP_OPS_SERVICE);             }             mMyUid = Process.myUid();             if  (info != null ) {                 setReadPermission(info.readPermission);                 setWritePermission(info.writePermission);                 setPathPermissions(info.pathPermissions);                 mExported = info.exported;                 mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0 ;                 setAuthorities(info.authority);             }             if  (Build.IS_DEBUGGABLE) {                 setTransportLoggingEnabled(Log.isLoggable(getClass().getSimpleName(),                         Log.VERBOSE));             }             ContentProvider.this .onCreate();         }     } 
最终我们在这发现回调了onCreate。这里我们只是根据函数名+源码注释+猜测去验证了他们两个的执行顺序,更多细枝末节就没看(其实是看不懂),如果大家想真正搞明白ContentProvider和Application完整的创建流程,大家可以去研究一下Android的源码。
知道了这个之后我们就能理解LeakCanary的做法了,翻其源码也确实是这样。
<?xml version="1.0"  encoding="utf-8" ?> <manifest      xmlns:android ="http://schemas.android.com/apk/res/android"      package ="com.squareup.leakcanary.objectwatcher"      >   <application >      <provider           android:name ="leakcanary.internal.MainProcessAppWatcherInstaller"          android:authorities ="${applicationId}.leakcanary-installer"          android:enabled ="@bool/leak_canary_watcher_auto_install"          android:exported ="false" />   </application >  </manifest > 
那可能有人会疑问,为啥它那定义的清单文件我这也能用捏?其实这是Gradle的功劳,一个app是只能有一个清单文件的,在构建应用的时候,Gradle会合并所有的清单文件,所有文件优先级如下
Product flavors 和构建类型所指定的清单文件。 
应用程序的主清单文件。 
类库的清单文件。 
 
具体的合并逻辑就涉及Gradle的东西了,这里就不说了。那好,那我们的第一个问题就解决了。
LeakCanary是如何监听的 现在我们看MainProcessAppWatcherInstaller
internal  class  MainProcessAppWatcherInstaller  : ContentProvider () {  override  fun  onCreate () Boolean  {     val  application = context!!.applicationContext as  Application     AppWatcher.manualInstall(application)     return  true    } } 
初始化了AppWatcher
@JvmOverloads fun  manualInstall (   application: Application ,   retainedDelayMillis: Long  = TimeUnit.SECONDS.toMillis(5 )   watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application) ) {   checkMainThread()   if  (isInstalled) {     throw  IllegalStateException(       "AppWatcher already installed, see exception cause for prior install call" , installCause     )   }   check(retainedDelayMillis >= 0 ) {     "retainedDelayMillis $retainedDelayMillis  must be at least 0 ms"    }   this .retainedDelayMillis = retainedDelayMillis   if  (application.isDebuggableBuild) {     LogcatSharkLog.install()   }      LeakCanaryDelegate.loadLeakCanary(application)      watchersToInstall.forEach {     it.install()   }      installCause = RuntimeException("manualInstall() first called here" ) } 
我们先不看核心组件,它比较复杂,我们先看看默认的检测器是如何对Activity、Fragment、Viewmodel等的监听的,先看appDefaultWatchers(application)这个生成默认检测器的方法
fun  appDefaultWatchers (   application: Application ,   reachabilityWatcher: ReachabilityWatcher  = objectWatcher )   return  listOf(     ActivityWatcher(application, reachabilityWatcher),     FragmentAndViewModelWatcher(application, reachabilityWatcher),     RootViewWatcher(reachabilityWatcher),     ServiceWatcher(reachabilityWatcher)   ) } 
创建各个检测器,传入objectWatcher和application,这四个监听器我们一个一个来看
ActivityWatcher class  ActivityWatcher (  private  val  application: Application,   private  val  reachabilityWatcher: ReachabilityWatcher ) : InstallableWatcher {   private  val  lifecycleCallbacks =     object  : Application.ActivityLifecycleCallbacks by  noOpDelegate() {       override  fun  onActivityDestroyed (activity: Activity )          reachabilityWatcher.expectWeaklyReachable(           activity, "${activity::class.java.name}  received Activity#onDestroy() callback"          )       }     }   override  fun  install ()      application.registerActivityLifecycleCallbacks(lifecycleCallbacks)   }   override  fun  uninstall ()      application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)   } } 
可以看到,通过Activity生命周期对的Application.ActivityLifecycleCallbacks回调来达到监听Activity的结束。其实就是在Activity快onDestroy的时候调用了Application.ActivityLifecycleCallbacks的onActivityDestroyed。然后在onActivityDestroyed里面调用了reachabilityWatcher的expectWeaklyReachable,reachabilityWatcher就是刚刚创建的时候传进来的,我们返回去看看reachabilityWatcher对expectWeaklyReachable的实现。跟踪发现,传进来的是objectWatcher,点击跟踪
val  objectWatcher = ObjectWatcher(  clock = { SystemClock.uptimeMillis() },   checkRetainedExecutor = {     check(isInstalled) {       "AppWatcher not installed"      }     mainHandler.postDelayed(it, retainedDelayMillis)   },   isEnabled = { true  } ) 
我们这里看到传入时间、一个Executor任务执行器,该任务执行器将传入的任务交给主线程的Handler延时处理,延时retainedDelayMillis就是刚刚初始化AppWatcher默认设置的5秒,看看ObjectWatcher对expectWeaklyReachable的实现,
@Synchronized  override  fun  expectWeaklyReachable (   watchedObject: Any ,   description: String  )   if  (!isEnabled()) {     return    }   removeWeaklyReachableObjects()   val  key = UUID.randomUUID()     .toString()   val  watchUptimeMillis = clock.uptimeMillis()   val  reference =     KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)   SharkLog.d {     "Watching "  +       (if  (watchedObject is  Class<*>) watchedObject.toString() else  "instance of ${watchedObject.javaClass.name} " ) +       (if  (description.isNotEmpty()) " ($description )"  else  "" ) +       " with key $key "    }   watchedObjects[key] = reference   checkRetainedExecutor.execute {     moveToRetained(key)   } } 
其实之前仿写的LeakCanary有点故意模仿LeakCanary的意思,这不LeakCanary里面也有观察区和留存区,只不过我们为了好获取泄漏对象而用了一个列表存储,这里没有列表存储,而是直接回调出去通知。每次观测之前先清除观察区已经被回收的对象
private  fun  removeWeaklyReachableObjects ()         var  ref: KeyedWeakReference?   do  {     ref = queue.poll() as  KeyedWeakReference?     if  (ref != null ) {       watchedObjects.remove(ref.key)     }   } while  (ref != null ) } 
然后就和我们刚仿写的差不多,生成一个唯一的UUID,将弱引用引用对象,再登记,只不过这里它记录了时间。最后执行moveToRetained(key)
@Synchronized  private  fun  moveToRetained (key: String )   removeWeaklyReachableObjects()   val  retainedRef = watchedObjects[key]   if  (retainedRef != null ) {     retainedRef.retainedUptimeMillis = clock.uptimeMillis()            onObjectRetainedListeners.forEach { it.onObjectRetained() }   } } 
这时候如果还有对象存在观察区,说明已经是可能内存泄漏的对象,然后记录此时的时间,再回调监听,onObjectRetainedListeners在哪设置的呢,在刚刚的核心组件里面,等会分析。
至此我们分析完LeakCanary是如何监听Activity生命周期和检测是否泄露的。接下来看Fragment和Viewmodel
FragmentAndViewModelWatcher class  FragmentAndViewModelWatcher (  private  val  application: Application,   private  val  reachabilityWatcher: ReachabilityWatcher ) : InstallableWatcher {   private  val  fragmentDestroyWatchers: List<(Activity) -> Unit > = run {     val  fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit >()            if  (SDK_INT >= O) {       fragmentDestroyWatchers.add(         AndroidOFragmentDestroyWatcher(reachabilityWatcher)       )     }     getWatcherIfAvailable(       ANDROIDX_FRAGMENT_CLASS_NAME,       ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,       reachabilityWatcher     )?.let {       fragmentDestroyWatchers.add(it)     }     getWatcherIfAvailable(       ANDROID_SUPPORT_FRAGMENT_CLASS_NAME,       ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,       reachabilityWatcher     )?.let {       fragmentDestroyWatchers.add(it)     }     fragmentDestroyWatchers   }   private  val  lifecycleCallbacks =     object  : Application.ActivityLifecycleCallbacks by  noOpDelegate() {       override  fun  onActivityCreated (          activity: Activity ,         savedInstanceState: Bundle ?       )                    for  (watcher in  fragmentDestroyWatchers) {           watcher(activity)         }       }     }   override  fun  install ()      application.registerActivityLifecycleCallbacks(lifecycleCallbacks)   }   override  fun  uninstall ()      application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)   }     ... } 
这里给activity注册一个监听,然后遍历里面的fragmentwatcher调用incoke设置fragment的监听,其他版本的fragmentDestroyWatcher就不看了和ActivityWatcher差不多,我们看看AndroidXFragmentDestroyWatcher
internal  class  AndroidXFragmentDestroyWatcher (  private  val  reachabilityWatcher: ReachabilityWatcher ) : (Activity) -> Unit  {   private  val  fragmentLifecycleCallbacks = object  : FragmentManager.FragmentLifecycleCallbacks() {     override  fun  onFragmentCreated (        fm: FragmentManager ,       fragment: Fragment ,       savedInstanceState: Bundle ?     )                ViewModelClearedWatcher.install(fragment, reachabilityWatcher)     }     override  fun  onFragmentViewDestroyed (        fm: FragmentManager ,       fragment: Fragment      )       val  view = fragment.view       if  (view != null ) {                    reachabilityWatcher.expectWeaklyReachable(           view, "${fragment::class.java.name}  received Fragment#onDestroyView() callback "  +           "(references to its views should be cleared to prevent leaks)"          )       }     }            override  fun  onFragmentDestroyed (        fm: FragmentManager ,       fragment: Fragment      )       reachabilityWatcher.expectWeaklyReachable(         fragment, "${fragment::class.java.name}  received Fragment#onDestroy() callback"        )     }   }   override  fun  invoke (activity: Activity )      if  (activity is  FragmentActivity) {       val  supportFragmentManager = activity.supportFragmentManager                supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true )                ViewModelClearedWatcher.install(activity, reachabilityWatcher)     }   } } 
其实跟activity的差不多,不过是通过activity设置fragment的监听,这里可能有个高阶函数大家没见过,也是invoke的一种用法吧,可以了解一下。好,我们现在来看看对viewmodel的监听过程:
ViewModelClearedWatcher internal  class  ViewModelClearedWatcher (  storeOwner: ViewModelStoreOwner,   private  val  reachabilityWatcher: ReachabilityWatcher ) : ViewModel() {            private  val  viewModelMap: Map<String, ViewModel>? = try  {            val  mMapField = ViewModelStore::class .java.getDeclaredField("mMap" )     mMapField.isAccessible = true      @Suppress("UNCHECKED_CAST" )      mMapField[storeOwner.viewModelStore] as  Map<String, ViewModel>   } catch  (ignored: Exception) {     null    }   override  fun  onCleared ()             viewModelMap?.values?.forEach { viewModel ->       reachabilityWatcher.expectWeaklyReachable(         viewModel, "${viewModel::class.java.name}  received ViewModel#onCleared() callback"        )     }   }   companion  object  {     fun  install (        storeOwner: ViewModelStoreOwner ,       reachabilityWatcher: ReachabilityWatcher      )                val  provider = ViewModelProvider(storeOwner, object  : Factory {         @Suppress("UNCHECKED_CAST" )          override  fun  <T : ViewModel?>  create (modelClass: Class <T >)            ViewModelClearedWatcher(storeOwner, reachabilityWatcher) as  T       })       provider.get (ViewModelClearedWatcher::class .java)     }   } } 
我们发现它居然是个viewmodel,而且它还把它自己插入了宿主的viewmodelstore。把自己当作间谍插入敌军来达到监听的目的。具体viewmodestore是如何管理同一宿主的多个viewmodel的这里就不解释了,大家可以看看viewmodel的源码(其实是我不会)。
好,再来看看rootView
RootViewWatcher 有人可能会问rootview也会发生内存泄露吗?会的,只是不常见,比如,此时我自定义了一个Toast弹窗,弹的是xml的布局,然后Toast被我声明成了静态方法,这时候如果我一弹窗,消失,之后LeakCanary就提醒你了,内存泄漏。自定义的toast代码如下
object  ToastUtil {    var  mToast: Toast? = null      fun  show ()          val  inflater = LayoutInflater.from(App.appContext)         val  toastView: View = inflater.inflate(R.layout.toast, null )         if  (mToast == null ) {             mToast = Toast(App.appContext)         }         mToast!!.setGravity(Gravity.TOP, 0 , 0 )         mToast!!.duration = Toast.LENGTH_SHORT         mToast!!.view = toastView         mToast!!.show()     } } 
这样的自定义Toast工具是内存泄漏的,为什么捏,因为object单例内部的变量是静态的,所以mToast是静态的,而它又引用了toastView,而toastView是rootView绘制的xml布局(原理见view的绘制),因此一系列下来导致弹完吐司之后,rootView绘制完了而还在被mToastView引用导致内存泄漏。事实上,你会发现,mToast.view已经被废弃,谷歌官方也不建议我们自定义toast,因为这样确实容易造成内存泄漏,更推荐我们自定义snackBar。
我们再看RootViewWatcher
class  RootViewWatcher (  private  val  reachabilityWatcher: ReachabilityWatcher ) : InstallableWatcher {   private  val  listener = OnRootViewAddedListener { rootView ->     ...     if  (trackDetached) {       rootView.addOnAttachStateChangeListener(object  : OnAttachStateChangeListener {         val  watchDetachedView = Runnable {           reachabilityWatcher.expectWeaklyReachable(             rootView, "${rootView::class.java.name}  received View#onDetachedFromWindow() callback"            )         }         override  fun  onViewAttachedToWindow (v: View )            mainHandler.removeCallbacks(watchDetachedView)         }         override  fun  onViewDetachedFromWindow (v: View )            mainHandler.post(watchDetachedView)         }       })     }   }   override  fun  install ()      Curtains.onRootViewsChangedListeners += listener   }   override  fun  uninstall ()      Curtains.onRootViewsChangedListeners -= listener   } } 
也不过是对rootview注册监听,具体的原理不说了,跟Activity的方式差不多。
ServiceWatcher 我们再看看ServiceWatcher
class  ServiceWatcher (private  val  reachabilityWatcher: ReachabilityWatcher) : InstallableWatcher {    ...   override  fun  install ()      checkMainThread()     check(uninstallActivityThreadHandlerCallback == null ) {       "ServiceWatcher already installed"      }     check(uninstallActivityManager == null ) {       "ServiceWatcher already installed"      }     try  {              swapActivityThreadHandlerCallback { mCallback ->         uninstallActivityThreadHandlerCallback = {           swapActivityThreadHandlerCallback {             mCallback           }         }                  Handler.Callback { msg ->                                                       if  (msg.obj !is  IBinder) {             return @Callback  false            }                      if  (msg.what == STOP_SERVICE) {             val  key = msg.obj as  IBinder             activityThreadServices[key]?.let {               onServicePreDestroy(key, it)             }           }                      mCallback?.handleMessage(msg) ?: false          }       }              swapActivityManager { activityManagerInterface, activityManagerInstance ->         uninstallActivityManager = {           swapActivityManager { _, _ ->             activityManagerInstance           }         }                  Proxy.newProxyInstance(           activityManagerInterface.classLoader, arrayOf(activityManagerInterface)         ) { _, method, args ->                      if  (METHOD_SERVICE_DONE_EXECUTING == method.name) {             val  token = args!![0 ] as  IBinder             if  (servicesToBeDestroyed.containsKey(token)) {                              onServiceDestroyed(token)             }           }                      try  {             if  (args == null ) {               method.invoke(activityManagerInstance)             } else  {               method.invoke(activityManagerInstance, *args)             }           } catch  (invocationException: InvocationTargetException) {             throw  invocationException.targetException           }         }       }     } catch  (ignored: Throwable) {       SharkLog.d(ignored) { "Could not watch destroyed services"  }     }   }     ...     private  fun  onServiceDestroyed (token: IBinder )           servicesToBeDestroyed.remove(token)?.also { serviceWeakReference ->       serviceWeakReference.get ()?.let { service ->                reachabilityWatcher.expectWeaklyReachable(           service, "${service::class.java.name}  received Service#onDestroy() callback"          )       }     }   }     ... } 
对Service的监控可能就比较奇怪了,因为Service没有对外公开Service生命周期的监听方式,所以只能hook ,所以这里面就涉及了Service的底层原理和hook技术,要真正理解的话得知道Service的底层原理的hook,这里简单说一下:启动Service的时候,在ActivityThread里面都会有记录,启动的Service都会存进mServices里面,onDestory的时候,AMS会用Handler发给ActivityThread告诉某某Service要销毁了,此时ActivityThread就会通过IActivityManger回调Service的onDestory方法,IActivityManger存储了四大组件的周期函数,所以通过它来调用Service的周期函数。因此原理也是这样,通过hook AMS发过来Service onDestory的信息记录Service,进而在IActivityManger回调Service onDestory的时候找到这个Service并监测。
监听之后是怎么处理的呢? 现在几个监测的类讲完了,那么第二个问题也解决了,接下来我们看看刚刚还没呢分析的核心组件
InternalLeakCanary @JvmOverloads fun  manualInstall (   application: Application ,   retainedDelayMillis: Long  = TimeUnit.SECONDS.toMillis(5 )   watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application) ) {   checkMainThread()   if  (isInstalled) {     throw  IllegalStateException(       "AppWatcher already installed, see exception cause for prior install call" , installCause     )   }   check(retainedDelayMillis >= 0 ) {     "retainedDelayMillis $retainedDelayMillis  must be at least 0 ms"    }   this .retainedDelayMillis = retainedDelayMillis   if  (application.isDebuggableBuild) {     LogcatSharkLog.install()   }      LeakCanaryDelegate.loadLeakCanary(application)      watchersToInstall.forEach {     it.install()   }      installCause = RuntimeException("manualInstall() first called here" ) } 
看看LeakCanaryDelegate.loadLeakCanary(application)
internal  object  LeakCanaryDelegate {  @Suppress("UNCHECKED_CAST" )    val  loadLeakCanary by  lazy {     try  {                val  leakCanaryListener = Class.forName("leakcanary.internal.InternalLeakCanary" )       leakCanaryListener.getDeclaredField("INSTANCE" )         .get (null ) as  (Application) -> Unit      } catch  (ignored: Throwable) {       NoLeakCanary     }   }   object  NoLeakCanary : (Application) -> Unit , OnObjectRetainedListener {     override  fun  invoke (application: Application )      }     override  fun  onObjectRetained ()      }   } } 
为什么要通过反射实例化咧?因为InternalLeakCanary在另一个模块,而他又是internal,所以只能通过反射了。那我们看看InternalLeakCanary复写的invoke方法
override  fun  invoke (application: Application )   _application = application   checkRunningInDebuggableBuild()        AppWatcher.objectWatcher.addOnObjectRetainedListener(this )        val  gcTrigger = GcTrigger.Default   val  configProvider = { LeakCanary.config }   val  handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME)   handlerThread.start()   val  backgroundHandler = Handler(handlerThread.looper)        heapDumpTrigger = HeapDumpTrigger(     application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger,     configProvider   )        application.registerVisibilityListener { applicationVisible ->     this .applicationVisible = applicationVisible     heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)   }   registerResumedActivityListener(application)        addDynamicShortcut(application)      mainHandler.post {                    backgroundHandler.post {       SharkLog.d {         when  (val  iCanHasHeap = HeapDumpControl.iCanHasHeap()) {           is  Yup -> application.getString(R.string.leak_canary_heap_dump_enabled_text)           is  Nope -> application.getString(             R.string.leak_canary_heap_dump_disabled_text, iCanHasHeap.reason()           )         }       }     }   } } 
原来我们之前在moveToRetain里回调的方法在这注册的。
@Synchronized  private  fun  moveToRetained (key: String )   removeWeaklyReachableObjects()   val  retainedRef = watchedObjects[key]   if  (retainedRef != null ) {     retainedRef.retainedUptimeMillis = clock.uptimeMillis()            onObjectRetainedListeners.forEach { it.onObjectRetained() }   } } 
那我们看看InternalLeakCanary对onObjectRetained()的实现,发现其调用的是scheduleRetainedObjectCheck()
fun  scheduleRetainedObjectCheck ()   if  (this ::heapDumpTrigger.isInitialized) {     heapDumpTrigger.scheduleRetainedObjectCheck()   } } 
调用的是scheduleRetainedObjectCheck()
fun  scheduleRetainedObjectCheck (   delayMillis: Long  = 0 L )   val  checkCurrentlyScheduledAt = checkScheduledAt   if  (checkCurrentlyScheduledAt > 0 ) {     return    }   checkScheduledAt = SystemClock.uptimeMillis() + delayMillis   backgroundHandler.postDelayed({     checkScheduledAt = 0      checkRetainedObjects()   }, delayMillis) } 
通过记录时间避免重复检测,然后向子线程post了一Runnable,瞅瞅checkRetainedObjects
private  fun  checkRetainedObjects ()        val  iCanHasHeap = HeapDumpControl.iCanHasHeap()   val  config = configProvider()   if  (iCanHasHeap is  Nope) {     if  (iCanHasHeap is  NotifyingNope) {                var  retainedReferenceCount = objectWatcher.retainedObjectCount         if  (retainedReferenceCount > 0 ) {           gcTrigger.runGc()           retainedReferenceCount = objectWatcher.retainedObjectCount         }         val  nopeReason = iCanHasHeap.reason()                  val  wouldDump = !checkRetainedCount(           retainedReferenceCount, config.retainedVisibleThreshold, nopeReason         )         if  (wouldDump) {           val  uppercaseReason = nopeReason[0 ].toUpperCase() + nopeReason.substring(1 )           onRetainInstanceListener.onEvent(DumpingDisabled(uppercaseReason))           showRetainedCountNotification(             objectCount = retainedReferenceCount,             contentText = uppercaseReason           )         }     } else  {       SharkLog.d {         application.getString(           R.string.leak_canary_heap_dump_disabled_text, iCanHasHeap.reason()         )       }     }     return    }   var  retainedReferenceCount = objectWatcher.retainedObjectCount   if  (retainedReferenceCount > 0 ) {     gcTrigger.runGc()     retainedReferenceCount = objectWatcher.retainedObjectCount   }   if  (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return    val  now = SystemClock.uptimeMillis()   val  elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis   if  (elapsedSinceLastDumpMillis < WAIT_BETWEEN_HEAP_DUMPS_MILLIS) {     onRetainInstanceListener.onEvent(DumpHappenedRecently)     showRetainedCountNotification(       objectCount = retainedReferenceCount,       contentText = application.getString(R.string.leak_canary_notification_retained_dump_wait)     )     scheduleRetainedObjectCheck(       delayMillis = WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis     )     return    }   dismissRetainedCountNotification()   val  visibility = if  (applicationVisible) "visible"  else  "not visible"         dumpHeap(     retainedReferenceCount = retainedReferenceCount,     retry = true ,     reason = "$retainedReferenceCount  retained objects, app is $visibility "    ) } 
先看看能不能堆转储,如果可以,就发送通知,如果不能,做了一系列操作,再尝试分析,其实这跟MAT有点类似,MAT也是不能直接就解析hprof文件,最后你会发现,最终调用的都是dumpHeap()方法,我们看dumpHeap方法
HeapDumpTrigger.kt private  fun  dumpHeap (   retainedReferenceCount: Int ,   retry: Boolean ,   reason: String  )   val  directoryProvider =     InternalLeakCanary.createLeakDirectoryProvider(InternalLeakCanary.application)   val  heapDumpFile = directoryProvider.newHeapDumpFile()   val  durationMillis: Long    if  (currentEventUniqueId == null ) {     currentEventUniqueId = UUID.randomUUID().toString()   }   try  {     InternalLeakCanary.sendEvent(DumpingHeap(currentEventUniqueId!!))     if  (heapDumpFile == null ) {       throw  RuntimeException("Could not create heap dump file" )     }     saveResourceIdNamesToMemory()     val  heapDumpUptimeMillis = SystemClock.uptimeMillis()     KeyedWeakReference.heapDumpUptimeMillis = heapDumpUptimeMillis     durationMillis = measureDurationMillis {       configProvider().heapDumper.dumpHeap(heapDumpFile)     }     if  (heapDumpFile.length() == 0L ) {       throw  RuntimeException("Dumped heap file is 0 byte length" )     }     lastDisplayedRetainedObjectCount = 0      lastHeapDumpUptimeMillis = SystemClock.uptimeMillis()     objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)     currentEventUniqueId = UUID.randomUUID().toString()            InternalLeakCanary.sendEvent(HeapDump(currentEventUniqueId!!, heapDumpFile, durationMillis, reason))   } catch  (throwable: Throwable) {     InternalLeakCanary.sendEvent(HeapDumpFailed(currentEventUniqueId!!, throwable, retry))     if  (retry) {       scheduleRetainedObjectCheck(         delayMillis = WAIT_AFTER_DUMP_FAILED_MILLIS       )     }     showRetainedCountNotification(       objectCount = retainedReferenceCount,       contentText = application.getString(         R.string.leak_canary_notification_retained_dump_failed       )     )     return    } } 
首先创建了文件夹然后是保存时间,ID啊一些的操作,最主要的是configProvider().heapDumper.dumpHeap(heapDumpFile),查找它的实现其实就是
object  AndroidDebugHeapDumper : HeapDumper {  override  fun  dumpHeap (heapDumpFile: File )      Debug.dumpHprofData(heapDumpFile.absolutePath)   } } 
用系统的Debug工具调出hprof文件。之后我们看导出成功回调做了什么事情
object  RemoteWorkManagerHeapAnalyzer : EventListener {  private  const  val  REMOTE_SERVICE_CLASS_NAME = "leakcanary.internal.RemoteLeakCanaryWorkerService"    internal  val  remoteLeakCanaryServiceInClasspath by  lazy {     try  {       Class.forName(REMOTE_SERVICE_CLASS_NAME)       true      } catch  (ignored: Throwable) {       false      }   }   override  fun  onEvent (event: Event )      if  (event is  HeapDump) {       val  application = InternalLeakCanary.application       val  heapAnalysisRequest =         OneTimeWorkRequest.Builder(RemoteHeapAnalyzerWorker::class .java).apply {           val  dataBuilder = Data.Builder()             .putString(ARGUMENT_PACKAGE_NAME, application.packageName)             .putString(ARGUMENT_CLASS_NAME, REMOTE_SERVICE_CLASS_NAME)           setInputData(event.asWorkerInputData(dataBuilder))           with(WorkManagerHeapAnalyzer) {             addExpeditedFlag()           }         }.build()       SharkLog.d { "Enqueuing heap analysis for ${event.file}  on WorkManager remote worker"  }       val  workManager = WorkManager.getInstance(application)       workManager.enqueue(heapAnalysisRequest)     }   } } 
我们发现根据回调的Event类型,回调到了这里,传进来的Event就是HeapDump,可以看到用WorkManager创建了一个异步任务WorkManagerHeapAnalyzer,然后执行
internal  class  RemoteHeapAnalyzerWorker (appContext: Context, workerParams: WorkerParameters) :  RemoteListenableWorker(appContext, workerParams) {   override  fun  startRemoteWork ()      val  heapDump = inputData.asEvent<HeapDump>()     val  result = SettableFuture.create<Result>()     heapAnalyzerThreadHandler.post {       val  doneEvent = AndroidDebugHeapAnalyzer.runAnalysisBlocking(heapDump, isCanceled = {         result.isCancelled       }) { progressEvent ->         if  (!result.isCancelled) {           InternalLeakCanary.sendEvent(progressEvent)         }       }       if  (result.isCancelled) {         SharkLog.d { "Remote heap analysis for ${heapDump.file}  was canceled"  }       } else  {         InternalLeakCanary.sendEvent(doneEvent)         result.set (Result.success())       }     }     return  result   }   override  fun  getForegroundInfoAsync ()      return  applicationContext.heapAnalysisForegroundInfoAsync()   } } 
heapAnalyzerThreadHandler实际上就是开启一个子线程然后执行runAnalysisBlocking,runAnalysisBlocking里面就是把文件交给了HeapAnalyzer去分析,HeapAnalyzer然后用Shark 分析hprof文件,具体Shark分析的原理就不说了,我也没怎么研究过,我觉得也没有用,会用就行了。Shark是LeakCanary的一个分析hprof文件的模块,因此我们也可以用shark开发一个用于线上的SDK,LeakCanary现在是不支持线上检测的。
用一张图总结一下它的原理其实就是
AppWatcher -> objectWatcher:检测到泄漏 objectWatcher -> InternalLeakCanary:5秒后gc还是没回收 InternalLeakCanary->HeapDumpTrigger:scheduleRetainedObjectCheck() HeapDumpTrigger->HeapAnalyzerThreadHandler:再强制GC一次,还是泄漏 HeapAnalyzerThreadHandler->HeapAnalyzer:dumpHeap() HeapAnalyzer->Shark分析: Shark分析 -> dataBase:发出通知,数据存进数据库 
可能还有人会疑另一个叫Leaks的app图标怎么生成的,其实它也不算是个app,它是你app的一部分,不信你把Leaks卸载了,你的app也会跟着卸载。实现这个很简单,用标签就能实现。示例如下 
<application      .  .  . >          . . .          <activity           android:name =".SecondActivity" />          <activity-alias           android:name ="com.jason.demo.dynamicshortcut.ShortcutLauncherActivity"          android:enabled ="true"          android:icon ="@mipmap/ic_launcher_alias"          android:label ="ActivityAlias"          android:targetActivity =".SecondActivity" >         <intent-filter >              <action  android:name ="android.intent.action.MAIN"  />              <category  android:name ="android.intent.category.LAUNCHER"  />          </intent-filter >      </activity-alias >       </application > 
其中,android:targetActivity 指定所必须的打开的目标 Activity,对应着一个在 AndroidManifest.xml 中申明的 <activity>。android:name 是别名的唯一名称,不引用实际类 。android:icon 以及 android:label 指定一个新的图标和标签给用户。之后就能看见你的app生成了两个图标。
你如果不想LeakCanary自动初始化也是可以的。设置如下
<?xml version="1.0"  encoding="utf-8" ?> <resources > <bool  name ="leak_canary_watcher_auto_install" > false</bool > </resources > 
然后在你想要初始化的地方AppWatcher.manualInstall即可
好了,LeakCanary源码算简单分析了一下,不算细致,可以借鉴一下。下面是一些常见的内存泄漏
单例模式引发的内存泄漏
原因:单例模式里的静态实例持有对象的引用,导致对象无法被回收,常见为持有Activity的引用
优化:改为持有Application的引用,或者不持有使用的时候传递。
集合操作不当引发的内存泄漏
原因:集合只增不减
优化:有对应的删除或卸载操作
线程的操作不当引发的内存泄漏
原因:线程持有对象的引用在后台执行,与对象的生命周期不一致
优化:静态实例+弱引用(WeakReference)方式,使其生命周期一致
匿名内部类/非静态内部类操作不当引发的内存泄漏
原因:内部类持有对象引用,导致无法释放,比如各种回调
优化:保持生命周期一致,改为静态实例+对象的弱引用方式(WeakReference)
常用的资源未关闭回收引发的内存泄漏
原因:BroadcastReceiver,File,Cursor,IO流,Bitmap等资源使用未关闭
优化:使用后有对应的关闭和卸载机制
Handler使用不当造成的内存泄漏
原因:Handler持有Activity的引用,其发送的Message中持有Handler的引用,当队列处理Message的时间过长会导致Handler无法被回收
优化:静态实例+弱引用(WeakReference)方式
 
好了,LeakCanary源码算简单分析了一下,不算细致,可以借鉴一下。