LeakCanary源码分析

从sample项目中的入口开始查看:


public class ExampleApplication extends Application {

  @Override 
  public void onCreate() {
    super.onCreate();
    if (LeakCanary.isInAnalyzerProcess(this)) {
      // This process is dedicated to LeakCanary for heap analysis.
      // You should not init your app in this process.
      return;
    }
    enabledStrictMode();
    LeakCanary.install(this);
  }

  private static void enabledStrictMode() {
    StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() //
        .detectAll() //
        .penaltyLog() //
        .penaltyDeath() //
        .build());
  }
}

if (LeakCanary.isInAnalyzerProcess(this)) {

      // This process is dedicated to LeakCanary for heap analysis.

      // You should not init your app in this process.

      return;

}

public static boolean isInAnalyzerProcess(Context context) {

    return isInServiceProcess(context, HeapAnalyzerService.class);

  }

  

  

   public static boolean isInServiceProcess(Context context, Class<? extends Service> serviceClass) {

    PackageManager packageManager = context.getPackageManager();

    PackageInfo packageInfo;

    try {

      packageInfo = packageManager.getPackageInfo(context.getPackageName(), GET_SERVICES);

    } catch (Exception e) {

      CanaryLog.d(e, "Could not get package info for %s", context.getPackageName());

      return false;

}

    String mainProcess = packageInfo.applicationInfo.processName;

 

    ComponentName component = new ComponentName(context, serviceClass);

    ServiceInfo serviceInfo;

    try {

      serviceInfo = packageManager.getServiceInfo(component, 0);//获取serviceClass服务信息

    } catch (PackageManager.NameNotFoundException ignored) {

      // Service is disabled.

      return false;

    }

    if (serviceInfo.processName.equals(mainProcess)) {

      CanaryLog.d("Did not expect service %s to run in main process %s", serviceClass, mainProcess);

      // Technically we are in the service process, but we're not in the service dedicated process.

      return false;

    }

 int myPid = android.os.Process.myPid();

    ActivityManager activityManager =

        (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);

    ActivityManager.RunningAppProcessInfo myProcess = null;

    List<ActivityManager.RunningAppProcessInfo> runningProcesses =

        activityManager.getRunningAppProcesses();

    if (runningProcesses != null) {

      for (ActivityManager.RunningAppProcessInfo process : runningProcesses) {

        if (process.pid == myPid) {

          myProcess = process;

          break;

        }

      }

    }

    if (myProcess == null) {

      CanaryLog.d("Could not find running process for %d", myPid);

      return false;

    }

    return myProcess.processName.equals(serviceInfo.processName);

  }


前面是检查当前app进程是否和内存分析服务的进程是否在同一进程,如果是,则不安装内

存检测。为什么是这样子的呢?

查看内存分析的服务,发现前面有一段注释

**
 * This service runs in a separate process to avoid slowing down the app process or making it run
 * out of memory.
 */
public final classHeapAnalyzerService extends IntentService

意思是这个服务运行在单独的进程避免使当前app运行慢或者内存溢出.由此可见内存分析服务分析过程中占用资源多

接下来是如果判断不在当前进程中,则进行安装.代码如下

public final classLeakCanary {

  /**
   * Creates a {
@linkRefWatcher} that works out of the box, and starts watching activity
   * references (on ICS+).
   */
  
public staticRefWatcher install(Application application) {
    returnrefWatcher(application).listenerServiceClass(DisplayLeakService.class)
        .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
        .buildAndInstall();
  }

 

首先创建一个AndoidRefWatchBuilder,这里用到创建者模式.

/** Builder to create a customized {@linkRefWatcher} with appropriate Android defaults. */
public static AndroidRefWatcherBuilder refWatcher(Context context) {
  return newAndroidRefWatcherBuilder(context);
}

然后设置一个显示分析内存分析后分析结果处理的监听器.这个监听器必须继承抽象类AbstractAnalysisResultService

/**
 * Sets a custom {
@linkAbstractAnalysisResultService} to listen to analysis results. This
 * overrides any call to {
@link#heapDumpListener(HeapDump.Listener)}.
 */
public AndroidRefWatcherBuilderlistenerServiceClass(
    Class<? extendsAbstractAnalysisResultService> listenerServiceClass) {
  returnheapDumpListener(newServiceHeapDumpListener(context,listenerServiceClass));
}

 

AbstractAnalysisResultService又继承了IntentService

public abstract classAbstractAnalysisResultServiceextends IntentService {

  private static finalString HEAP_DUMP_EXTRA= "heap_dump_extra";
  private static finalString RESULT_EXTRA= "result_extra";

ServiceHeapDumpListener代码如下:

publicServiceHeapDumpListener(Context context,
    Class<?extends AbstractAnalysisResultService> listenerServiceClass) {
  setEnabled(context,listenerServiceClass, true);
  setEnabled(context,HeapAnalyzerService.class, true);
  this.listenerServiceClass= checkNotNull(listenerServiceClass,"listenerServiceClass");
  this.context= checkNotNull(context,"context").getApplicationContext();
}

里面的setEnabled方法设置该服务可用(以前我们写设置服务不可用,直接简单粗暴的调用stopService方法,看大神写的代码才发现大神不愧是大神,考虑的那么细致).

public static voidsetEnabled(Context context, finalClass<?> componentClass,
    final boolean enabled) {
  finalContext appContext = context.getApplicationContext();
  executeOnFileIoThread(newRunnable() {
    @Overridepublic void run() {
      setEnabledBlocking(appContext,componentClass,enabled);
    }
  });
}

public static void setEnabledBlocking(Context appContext,Class<?> componentClass,
    boolean enabled) {
  ComponentName component =new ComponentName(appContext,componentClass);
  PackageManager packageManager = appContext.getPackageManager();
  int newState = enabled ?COMPONENT_ENABLED_STATE_ENABLED: COMPONENT_ENABLED_STATE_DISABLED;
  // Blocks on IPC.
  packageManager.setComponentEnabledSetting(component,newState,DONT_KILL_APP);
}

继续往下看

/**
 * Creates a {
@linkRefWatcher} that works out of the box, and starts watching activity
 * references (on ICS+).
 */
public static RefWatcher install(Application application) {
  returnrefWatcher(application).listenerServiceClass(DisplayLeakService.class)
      .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
      .buildAndInstall();
}

 

创建了一个ExcludedRefs.Builder的建造者。

/**
 * This returns the references in the leak path that can be ignored for app developers. This
 * doesn't mean there is no memory leak, to the contrary. However, some leaks are caused by bugs
 * in AOSP or manufacturer forks of AOSP. In such cases, there is very little we can do as app
 * developers except by resorting to serious hacks, so we remove the noise caused by those leaks.
 */
public static ExcludedRefs.Builder createAppDefaults() {
  returncreateBuilder(EnumSet.allOf(AndroidExcludedRefs.class));
}

添加了一个AndroidExcludedRefs.class

public staticExcludedRefs.BuildercreateBuilder(EnumSet<AndroidExcludedRefs> refs) {
  ExcludedRefs.Builder excluded = ExcludedRefs.builder();
  for (AndroidExcludedRefs ref : refs) {
    if(ref.applies) {
      ref.add(excluded);
      ((ExcludedRefs.BuilderWithParams) excluded).named(ref.name());
    }
  }
  returnexcluded;
}

把所有要分析的AndroidExcludedRefs根据当前手机SDK版本和ROM作了过滤,注释里面也说得很清楚,随着时间的推移,很多SDK 和厂商 ROM 中的内存泄露问题已经被尽快修复了

/**
 * This class is a work in progress. You can help by reporting leak traces that seem to be caused
 * by the Android SDK, here: https://github.com/square/leakcanary/issues/new
 *
 * We filter on SDK versions and Manufacturers because many of those leaks are specific to a given
 * manufacturer implementation, they usually share their builds across multiple models, and the
 * leaks eventually get fixed in newer versions.
 *
 * Most app developers should use {
@link#createAppDefaults()}. However, you can also pick the
 * leaks you want to ignore by creating an {
@linkEnumSet} that matches your needs and calling
 * {
@link#createBuilder(EnumSet)}
 */
@SuppressWarnings({"unused","WeakerAccess" }) // Public API.
public enum AndroidExcludedRefs

继续往往下看..

/**
 * Creates a {
@linkRefWatcher} instance and starts watching activity references (on ICS+).
 */
public RefWatcherbuildAndInstall() {
  RefWatcher refWatcher = build();
  if (refWatcher !=DISABLED) {
    LeakCanary.enableDisplayLeakActivity(context);
    ActivityRefWatcher.install((Application)context,refWatcher);
  }
  returnrefWatcher;
}

这个和前面的设置服务调用的是同一个方法

public static voidenableDisplayLeakActivity(Context context) {
  setEnabled(context,DisplayLeakActivity.class, true);
}

 

public static voidinstall(Application application,RefWatcher refWatcher) {
  newActivityRefWatcher(application,refWatcher).watchActivities();
}

 

public voidwatchActivities() {
  // Make sure you don't get installed twice.
  stopWatchingActivities();
  application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}

这里注册了一个activity什么周期的回调

private finalApplication.ActivityLifecycleCallbackslifecycleCallbacks =
    newApplication.ActivityLifecycleCallbacks() {
      @Overridepublic void onActivityCreated(Activity activity,Bundle savedInstanceState) {
      }

      @Overridepublic void onActivityStarted(Activity activity) {
      }

      @Overridepublic void onActivityResumed(Activity activity) {
      }

      @Overridepublic void onActivityPaused(Activity activity) {
      }

      @Overridepublic void onActivityStopped(Activity activity) {
      }

      @Overridepublic void onActivitySaveInstanceState(Activity activity,Bundle outState) {
      }

      @Overridepublic void onActivityDestroyed(Activity activity) {
        ActivityRefWatcher.this.onActivityDestroyed(activity);
      }
    };

 

这个回调有什么用呢? 这个回调可就厉害了,app里面所有activity什么周期的都会在回调,可看到这里的回调方法都对应的activity的生命周期方法。举个栗子,app里面随便在哪里调用startActivity. Activity执行onCreate方法时会回调onActivityCreated()方法。以后写退出app的辅助类是,就不用在BaseActivity里面的onCreate()方法里面添加当前activity,onDestory里面移除当前activity。当然这只是这个回调在其他地方的用处。扯远了.

 

最最关键的代码来了.将要销毁的activity添加到观察里面

voidonActivityDestroyed(Activity activity) {
  refWatcher.watch(activity);
}

调用

/**
 * Identical to {
@link#watch(Object, String)} with an empty string reference name.
 *
 *
@see#watch(Object, String)
 */
public void watch(Object watchedReference) {
  watch(watchedReference,"");
}

最终调用。生成一个随机的UUID当做key。把key添加到retainedKeys set集合里面,key和要检测的对象封装成KeyedWeakReference

/**
 * Watches the provided references and checks if it can be GCed. This method is non blocking,
 * the check is done on the {
@linkWatchExecutor} this {@linkRefWatcher} has been constructed
 * with.
 *
 *
@paramreferenceNameAn logical identifier for the watched object.
 */
public void watch(Object watchedReference,String referenceName) {
  if(this== DISABLED) {
    return;
  }
  checkNotNull(watchedReference,"watchedReference");
  checkNotNull(referenceName,"referenceName");

//通过两个差值计算某些代码执行时间,System.currentTimeMillis要精确
  final long watchStartNanoTime = System.nanoTime();
  String key = UUID.randomUUID().toString();
  retainedKeys.add(key);
  final KeyedWeakReference reference =
      newKeyedWeakReference(watchedReference,key,referenceName,queue);

  ensureGoneAsync(watchStartNanoTime,reference);
}

 

特定作了一个实验

longstartcurrentTimeMillis=System.currentTimeMillis();//精确到毫秒
Thread.sleep(100);
System.out.println(System.currentTimeMillis()-startcurrentTimeMillis);

long startnanoTime=System.nanoTime();//精确到毫微秒
Thread.sleep(100);
System.out.println(System.nanoTime()-startnanoTime);

输出结果

100

100126792

原来它就是可以精确到后面6位数.就和圆周率后面的小数点一样,越多越精确。虽然说毫秒级的误差应该是可以忽略不计的,但也让我们知道了原来还有比毫秒更精确的一个方法的存在,说不定万一哪天就遇到了呢。

继续看代码.

private voidensureGoneAsync(final longwatchStartNanoTime, finalKeyedWeakReference reference) {
  watchExecutor.execute(newRetryable() {
    @Overridepublic Retryable.Resultrun() {
      returnensureGone(reference,watchStartNanoTime);
    }
  });
}

最终检测是否内存泄露的

@SuppressWarnings("ReferenceEquality")// Explicitly checking for named null.
Retryable.Result ensureGone(finalKeyedWeakReference reference, final longwatchStartNanoTime) {
  longgcStartNanoTime = System.nanoTime();
  long watchDurationMs =NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
  //查看弱引用所引用的对象是否被回收了,如果被回收了则retainedKeys集合里面移除当前弱引用的key
  removeWeaklyReachableReferences();

  if (debuggerControl.isDebuggerAttached()) {
    // The debugger can create false leaks.
    returnRETRY;
  }

//判断弱引用的key是否还在,如果不在了,则说明该对象已经被回收了
  if(gone(reference)) {
    returnDONE;
  }

//调用gc方法
  gcTrigger.runGc();

//再判断
  removeWeaklyReachableReferences();

//如果对象依然存在,则说明该对象已经泄露了,然后获取dumpHeap文件,分析dumHeap文件
  if (!gone(reference)) {
    longstartDumpHeap = System.nanoTime();
    long gcDurationMs =NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);

    File heapDumpFile =heapDumper.dumpHeap();
    if (heapDumpFile ==RETRY_LATER) {
      // Could not dump the heap.
      returnRETRY;
    }
    longheapDumpDurationMs =NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);

//解析dumHeap文件
    heapdumpListener.analyze(
        newHeapDump(heapDumpFile,reference.key,reference.name,excludedRefs,watchDurationMs,
            gcDurationMs,heapDumpDurationMs));
  }
  returnDONE;
}

 

private voidremoveWeaklyReachableReferences() {
  // WeakReferences are enqueued as soon as the object to which they point to becomes weakly
  // reachable. This is before finalization or garbage collection has actually happened.
  KeyedWeakReference ref;

//如果弱引用所引用的对象没有被垃圾回收器回收了,queue.poll()返回null
  while ((ref = (KeyedWeakReference)queue.poll()) !=null) {
    retainedKeys.remove(ref.key);
  }
}

//检查当前引用的key是否在集合当中

private booleangone(KeyedWeakReference reference) {
  return!retainedKeys.contains(reference.key);
}

如果在还集合当中,运行gc

@Overridepublic void runGc() {
  // Code taken from AOSP FinalizationTest:
  // https://android.googlesource.com/platform/libcore/+/master/support/src/test/java/libcore/
  // java/lang/ref/FinalizationTester.java
  // System.gc() does not garbage collect every time. Runtime.gc() is
  // more likely to perfom a gc.
  Runtime.getRuntime().gc();
  enqueueReferences();
  System.runFinalization();
}

 

最费解的当属弱引用和其引用列队的联合使用,然后判断当前弱引用所应用的对象是否被回收了。

这里特例举个栗子说明弱引用如何判断所引用的对象是否被回收了

 

Person person=newPerson();
        ReferenceQueue queue=newReferenceQueue();
        final KeyedWeakReference reference =
                newKeyedWeakReference(person,"123","456",queue);
        person=null;//显示将person置为null
        System.out.println("queue.poll():"+queue.poll());
//        System.out.println("queue.get():"+reference.get());
        System.gc();
        try {
            Thread.sleep(500);
        }catch (InterruptedException e) {
            throw newAssertionError();
        }
        System.runFinalization();
        System.out.println("queue.poll():"+queue.poll());

 

 

输出结果

queue.poll():null

finalize.....

queue.poll():com.example.lib.KeyedWeakReference@75b84c92

当我们将person置为null时,垃圾回收器并不会立即回收person对象,所以第一个poll的结果是null.当我们调用gc方法后,垃圾回收器发现personnull(因为调用gc后垃圾回收器并不会立即执行,所以加延迟),于是就回收person对象,所以第二次打印的结果就不一样了。


感谢一下文章:

https://www.liaohuqiu.net/cn/posts/leak-canary-read-me/

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页