-
LeakCanary源码解析
2019-02-24 22:58:13源码结构 leakcanary-watcher: 这是一个通用的内存检测器,对外提供一个 RefWatcher#watch(Object watchedReference),它不仅能够检测Activity,还能监测任意常规的 Java Object 的泄漏情况。 ...开源库路径https://github.com/square/leakcanary
源码结构
- leakcanary-watcher: 这是一个通用的内存检测器,对外提供一个 RefWatcher#watch(Object watchedReference),它不仅能够检测Activity,还能监测任意常规的 Java Object 的泄漏情况。
- leakcanary-android: 这个 module 是与 Android 的接入点,用来专门监测 Activity 的泄漏情况,内部使用了 application#registerActivityLifecycleCallbacks 方法来监听 onDestory 事件,然后利用 leakcanary-watcher 来进行弱引用+手动 GC 机制进行监控。
- leakcanary-analyzer: 这个 module 提供了 HeapAnalyzer,用来对 dump 出来的内存进行分析并返回内存分析结果AnalysisResult,内部包含了泄漏发生的路径等信息供开发者寻找定位。
- leakcanary-android-no-op: 这个 module 是专门给 release 的版本用的,内部只提供了两个完全空白的类 LeakCanary 和 RefWatcher,这两个类不会做任何内存泄漏相关的分析。因为 LeakCanary 本身会由于不断 gc 影响到 app 本身的运行,而且主要用于开发阶段的内存泄漏检测。因此对于 release 则可以 disable 所有泄漏分析。
原理简介
LeakCanary的原理非常简单。正常情况下一个Activity在执行Destroy之后就要销毁,LeakCanary做的就是在一个Activity/Fragment Destroy之后将它放在一个WeakReference中,然后将这个WeakReference关联到一个ReferenceQueue,查看ReferenceQueue是否存在Activity的引用,如果不在这个队列中,执行一些GC清洗操作,再次查看。如果不存在则证明该Activity/Fragment泄漏了,之后Dump出heap信息,并用haha这个开源库去分析泄漏路径。
基本原理图示
源码分析
简单分析,只分析如何实现内存泄漏检测的基本思路
第一步:入口函数分析LeakCanary.install(Application application)
集成使用LeakCanary基本上是在Application onCreate中调用即可LeakCanary.install(this); ,这是总的入口函数,第一步分析就从这里开始。
/** * Creates a {@link RefWatcher} that works out of the box, and starts watching activity * references (on ICS+). */ public static @NonNull RefWatcher install(@NonNull Application application) { //返回的是AndroidRefWatcherBuilder继承自RefWatcher对象 return refWatcher(application).listenerServiceClass(DisplayLeakService.class) .excludedRefs(AndroidExcludedRefs.createAppDefaults().build()) .buildAndInstall(); } 分析一:返回AndroidRefWatcherBuilder对象 refWatcher(application) 分析二:调用返回AndroidRefWatcherBuilder.buildAndInstall buildAndInstall 复制代码
分析一: refWatcher(application)
该方法调用的refWatcher返回了AndroidRefWatcherBuilder对象:
public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) { return new AndroidRefWatcherBuilder(context); } 复制代码
分析二:AndroidRefWatcherBuilder.buildAndInstall
进行设置监控所需要的相关系统lifeCycle回调,包含ActivityLifecycleCallbacks以及FragmentLifecycleCallbacks。
/** * Creates a {@link RefWatcher} instance and makes it available through {@link * LeakCanary#installedRefWatcher()}. * * Also starts watching activity references if {@link #watchActivities(boolean)} was set to true. * * @throws UnsupportedOperationException if called more than once per Android process. */ public @NonNull RefWatcher buildAndInstall() { //buildAndInstall只允许调用一次 if (LeakCanaryInternals.installedRefWatcher != null) { throw new UnsupportedOperationException("buildAndInstall() should only be called once."); } //创建用于实际处理判断内存泄漏的监控对象RefWatcher,放在内容第二步分析 RefWatcher refWatcher = build(); if (refWatcher != DISABLED) { LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true); if (watchActivities) { //watchActivities默认为true,开始activity引用的监控 ActivityRefWatcher.install(context, refWatcher); } if (watchFragments) { //watchFragments默认为true,开始fragment引用的监控 FragmentRefWatcher.Helper.install(context, refWatcher); } } //赋值installedRefWatcher,用于判断已创建成功 LeakCanaryInternals.installedRefWatcher = refWatcher; return refWatcher; } 分析三:设置activity资源泄漏监控: ActivityRefWatcher.install 分析四:设置Fragment资源泄漏监控: FragmentRefWatcher.Helper.install 复制代码
分析三:ActivityRefWatcher.install
这边主要是对app注册了个ActivityLifecycleCallbacks,在每次activity被销毁后都会回调到onActivityDestroyed,在onActivityDestroyed中获取在理论上即将被销毁的activity对象,调用refWatcher.watch检测其是否发生泄漏
public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) { Application application = (Application) context.getApplicationContext(); ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher); //注册个lifecycleCallbacks,在里面分析activity的内存泄漏问题 application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks); } //app所有activity生命周期结束自动回调 private final Application.ActivityLifecycleCallbacks lifecycleCallbacks = new ActivityLifecycleCallbacksAdapter() { @Override public void onActivityDestroyed(Activity activity) { //调用的还是refWatcher操作,ActivityRefWatcher只是为了activity周期监听 refWatcher.watch(activity); } }; 下文第二步分析activity是否存在内存泄漏: refWatcher.watch(activity) 复制代码
分析四:FragmentRefWatcher.Helper.install
这边主要用于ActivityLifecycleCallbacks中各个activity创建的时候, 获取到activity对应的FragmentManager注册FragmentLifecycleCallbacks.后续当有Fragment消耗触发onFragmentViewDestroyed或者onFragmentDestroyed时,则获取理论上即将被销毁的view/fragment对象,调用refWatcher.watch检测其是否发生泄漏。
public static void install(Context context, RefWatcher refWatcher) { List<FragmentRefWatcher> fragmentRefWatchers = new ArrayList<>(); if (SDK_INT >= O) { //添加了个AndroidOFragmentRefWatcher用于对android.app.FragmentManager设置FragmentLifecycleCallbacks fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher)); } try { //反射添加SupportFragmentRefWatcher用于对android.support.v4.app.FragmentManager设置FragmentLifecycleCallbacks Class<?> fragmentRefWatcherClass = Class.forName(SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME); Constructor<?> constructor = fragmentRefWatcherClass.getDeclaredConstructor(RefWatcher.class); FragmentRefWatcher supportFragmentRefWatcher = (FragmentRefWatcher) constructor.newInstance(refWatcher); fragmentRefWatchers.add(supportFragmentRefWatcher); } catch (Exception ignored) { ignored.printStackTrace(); } if (fragmentRefWatchers.size() == 0) { return; } Helper helper = new Helper(fragmentRefWatchers); //这边再次注册了另外一个ActivityLifecycleCallbacks Application application = (Application) context.getApplicationContext(); application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks); } //该ActivityLifecycleCallbacks主要在onActivityCreated回调的时候执行上面添加的FragmentRefWatcher.watchFragments方法 private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks = new ActivityLifecycleCallbacksAdapter() { @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { for (FragmentRefWatcher watcher : fragmentRefWatchers) { watcher.watchFragments(activity); } } }; 分析五:fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher)): 用于对android.app.FragmentManager设置FragmentLifecycleCallbacks 分析六:fragmentRefWatchers.add(supportFragmentRefWatcher): 用于对android.support.v4.app.FragmentManager设置FragmentLifecycleCallbacks 复制代码
分析五:fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher)):
添加了个AndroidOFragmentRefWatcher用于对android.app.FragmentManager设置FragmentLifecycleCallbacks,后续在fragment生命周期结束时获取并判断是否存在fragment内存泄漏。
AndroidOFragmentRefWatcher.watchFragments:
private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks = new FragmentManager.FragmentLifecycleCallbacks() { @Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment fragment) { //检测即将被回收的view是否存在泄漏 View view = fragment.getView(); if (view != null) { refWatcher.watch(view); } } @Override public void onFragmentDestroyed(FragmentManager fm, Fragment fragment) { //检测即将被回收的fragment是否存在泄漏 refWatcher.watch(fragment); } }; @Override public void watchFragments(Activity activity) { FragmentManager fragmentManager = activity.getFragmentManager(); //对activity注册FragmentLifecycleCallbacks生命周期监听 fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true); } 复制代码
分析六:fragmentRefWatchers.add(supportFragmentRefWatcher):
添加了个SupportFragmentRefWatcher用于对android.support.v4.app.FragmentManager设置FragmentLifecycleCallbacks,后续在fragment生命周期结束时获取并判断是否存在fragment内存泄漏。
SupportFragmentRefWatcher.watchFragments:
private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks = new FragmentManager.FragmentLifecycleCallbacks() { @Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment fragment) { //检测即将被回收的view是否存在泄漏 View view = fragment.getView(); if (view != null) { refWatcher.watch(view); } } @Override public void onFragmentDestroyed(FragmentManager fm, Fragment fragment) { //检测即将被回收的fragment是否存在泄漏 refWatcher.watch(fragment); } }; @Override public void watchFragments(Activity activity) { if (activity instanceof FragmentActivity) { //对activity注册FragmentLifecycleCallbacks生命周期监听 FragmentManager supportFragmentManager = ((FragmentActivity) activity).getSupportFragmentManager(); supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true); } } 复制代码
第二步:内存是否泄漏判断refWatcher.watch()
首先从RefWatcher对象的创建开始
/** Creates a {@link RefWatcher}. */ public final RefWatcher build() { if (isDisabled()) { return RefWatcher.DISABLED; } if (heapDumpBuilder.excludedRefs == null) { heapDumpBuilder.excludedRefs(defaultExcludedRefs()); } HeapDump.Listener heapDumpListener = this.heapDumpListener; if (heapDumpListener == null) { heapDumpListener = defaultHeapDumpListener(); } //默认为null DebuggerControl debuggerControl = this.debuggerControl; if (debuggerControl == null) { debuggerControl = defaultDebuggerControl(); } HeapDumper heapDumper = this.heapDumper; if (heapDumper == null) { heapDumper = defaultHeapDumper(); } //设置默认的监控执行处理器defaultWatchExecutor,调用AndroidRefWatcherBuilder.defaultWatchExecutor()获取 WatchExecutor watchExecutor = this.watchExecutor; if (watchExecutor == null) { watchExecutor = defaultWatchExecutor(); } //获取Gc处理器RefWatcherBuilder.defaultGcTrigger() GcTrigger gcTrigger = this.gcTrigger; if (gcTrigger == null) { gcTrigger = defaultGcTrigger(); } if (heapDumpBuilder.reachabilityInspectorClasses == null) { heapDumpBuilder.reachabilityInspectorClasses(defaultReachabilityInspectorClasses()); } //返回内存泄漏监控处理者RefWatcher return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener, heapDumpBuilder); } 分析七:defaultWatchExecutor(); 复制代码
分析七:defaultWatchExecutor
AndroidWatchExecutor对象的创建:
//默认延时参数5秒 private static final long DEFAULT_WATCH_DELAY_MILLIS = SECONDS.toMillis(5); @Override protected @NonNull WatchExecutor defaultWatchExecutor() { return new AndroidWatchExecutor(DEFAULT_WATCH_DELAY_MILLIS); } 复制代码
AndroidWatchExecutor实现的功能
AndroidWatchExecutor主要是做了一个简单的延时功能,因为activity、fragment等处罚ondestroy时,这些对象理论上即将被回收,但是还未被回收,所以AndroidWatchExecutor默认将检测任务发送到异步线程中做了个5秒的延时,注意这边是在异步线程,不阻塞主线程。在延时时间到了后,将检测任务再发送回主线程进行检测,注意这边之所以再发送回主线程,是因为gc操作只能在主线程触发。
AndroidWatchExecutor类: public final class AndroidWatchExecutor implements WatchExecutor { static final String LEAK_CANARY_THREAD_NAME = "LeakCanary-Heap-Dump"; private final Handler mainHandler; private final Handler backgroundHandler; private final long initialDelayMillis; private final long maxBackoffFactor; public AndroidWatchExecutor(long initialDelayMillis) { //创建运行与主线程的mainHandler mainHandler = new Handler(Looper.getMainLooper()); HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME); handlerThread.start(); //创建运行于后台线程的backgroundHandler backgroundHandler = new Handler(handlerThread.getLooper()); //默认为5s this.initialDelayMillis = initialDelayMillis; maxBackoffFactor = Long.MAX_VALUE / initialDelayMillis; } @Override public void execute(@NonNull Retryable retryable) { if (Looper.getMainLooper().getThread() == Thread.currentThread()) { //当前是主线程则执行waitForIdle waitForIdle(retryable, 0); } else { //当前是后台线程则执行postWaitForIdle postWaitForIdle(retryable, 0); } } private void postWaitForIdle(final Retryable retryable, final int failedAttempts) { //将检测任务Retryable post到主线程中去执行 mainHandler.post(new Runnable() { @Override public void run() { waitForIdle(retryable, failedAttempts); } }); } private void waitForIdle(final Retryable retryable, final int failedAttempts) { // This needs to be called from the main thread. //当主线程空闲时则执行postToBackgroundWithDelay Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() { @Override public boolean queueIdle() { postToBackgroundWithDelay(retryable, failedAttempts); return false; } }); } private void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) { long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor); long delayMillis = initialDelayMillis * exponentialBackoffFactor; //延时5秒执行Retryable检测 backgroundHandler.postDelayed(new Runnable() { @Override public void run() { Retryable.Result result = retryable.run(); if (result == RETRY) { postWaitForIdle(retryable, failedAttempts + 1); } } }, delayMillis); } } 复制代码
判断是否存在内存泄漏调用RefWatcher.watch:
public void watch(Object watchedReference, String referenceName) { if (this == DISABLED) { return; } checkNotNull(watchedReference, "watchedReference"); checkNotNull(referenceName, "referenceName"); //开始检测的时间 final long watchStartNanoTime = System.nanoTime(); //产生随机的key , 作为需要检测的对象的唯一标识 String key = UUID.randomUUID().toString(); //保存该key retainedKeys.add(key); //创建对应的对需要监控的watchedReference对象的弱引用并与ReferenceQueue绑定 final KeyedWeakReference reference = new KeyedWeakReference(watchedReference, key, referenceName, queue); //开始确认该对象是否被回收了 ensureGoneAsync(watchStartNanoTime, reference); } 复制代码
做了个简单的线程判断ensureGoneAsync
这边看到使用到了上面watchExecutor延时5秒后,再执行ensureGone
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) { watchExecutor.execute(new Retryable() { @Override public Retryable.Result run() { return ensureGone(reference, watchStartNanoTime); } }); } 复制代码
确认是否回收ensureGone
该函数执行的一个基本操作就是: 1.首先判断ReferenceQueue是否存在要检测内存泄漏的reference对象,不存在则代表可能发生泄漏
2.主动触发一次gc,进行内存回收
3.再次判断ReferenceQueue是否存在要检测内存泄漏的reference对象,不存在则代表可能发生泄漏
4.若发生泄漏则dump出内存hprof文件,进行分析,从中分析出内存泄漏的路径
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) { //gc准备开启的时间 long gcStartNanoTime = System.nanoTime(); //开始监控到准备gc的时间,大概5秒多,因为前边延时5秒 long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime); //移除已经被回收内存的监控对象的Key removeWeaklyReachableReferences(); if (debuggerControl.isDebuggerAttached()) { // The debugger can create false leaks. return RETRY; } //判断该reference对象是否被回收了,如果已经被回收,返回DONE, if (gone(reference)) { return DONE; } //如果尚未被回收,则主动触发一次gc gcTrigger.runGc(); //移除已经被回收内存的监控对象的Key removeWeaklyReachableReferences(); //判断该reference对象是否被回收了,如果已经被回收,返回DONE, if (!gone(reference)) { //该reference对象尚未被回收 long startDumpHeap = System.nanoTime(); long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime); //主动dump出内存Hprof文件 File heapDumpFile = heapDumper.dumpHeap(); if (heapDumpFile == RETRY_LATER) { // Could not dump the heap. return RETRY; } long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap); HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key) .referenceName(reference.name) .watchDurationMs(watchDurationMs) .gcDurationMs(gcDurationMs) .heapDumpDurationMs(heapDumpDurationMs) .build(); //将hprof进行分析出泄漏的点并通过ui通知用户 heapdumpListener.analyze(heapDump); } return DONE; } 复制代码
参考
allenwu.itscoder.com/leakcanary-…
公众号
-
LeakCanary 源码解析
2020-03-14 18:14:10LeakCanary 是什么? LeakCanary是Square公司基于MAT开源的一个工具,用来检测Android App中的内存泄露问题。官方地址:https://github.com/square/leakcanary 工作原理分析: 1.怎么检测内存泄漏?什么时候去检测...LeakCanary 是什么?
LeakCanary是Square公司基于MAT开源的一个工具,用来检测Android App中的内存泄露问题。官方地址:https://github.com/square/leakcanary
工作原理分析:
1.怎么检测内存泄漏?什么时候去检测内存泄漏?
//注册生命周期 在onDestory 的时候 观察Activity 对象 private final Application.ActivityLifecycleCallbacks lifecycleCallbacks = new ActivityLifecycleCallbacksAdapter() { @Override public void onActivityDestroyed(Activity activity) { //开始观察 refWatcher.watch(activity); } }; // 此方法为停止观察activity 也就是不在监听回掉 public void stopWatchingActivities() { application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks); }
自己去跟源码就会发现,LeakCanary 会 注册一个ActivityLifecycleCallbacks ,当监听到activity Ondestory 的时候,就会去观察这个activity 是不是泄漏了,有没有被GC回收。
具体怎么观察Activity 有没有被回收?
/** * Watches the provided references and checks if it can be GCed. This method is non blocking, * the check is done on the {@link WatchExecutor} this {@link RefWatcher} has been constructed * with. * * @param referenceName An logical identifier for the watched object. */ public void watch(Object watchedReference, String referenceName) { if (this == DISABLED) { return; } checkNotNull(watchedReference, "watchedReference"); checkNotNull(referenceName, "referenceName"); final long watchStartNanoTime = System.nanoTime(); String key = UUID.randomUUID().toString(); retainedKeys.add(key); final KeyedWeakReference reference = new KeyedWeakReference(watchedReference, key, referenceName, queue); ensureGoneAsync(watchStartNanoTime, reference); } Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) { long gcStartNanoTime = System.nanoTime(); long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime); removeWeaklyReachableReferences(); if (debuggerControl.isDebuggerAttached()) { // The debugger can create false leaks. return RETRY; } if (gone(reference)) { return DONE; } gcTrigger.runGc(); removeWeaklyReachableReferences(); if (!gone(reference)) { long startDumpHeap = System.nanoTime(); long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime); File heapDumpFile = heapDumper.dumpHeap(); if (heapDumpFile == RETRY_LATER) { // Could not dump the heap. return RETRY; } long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap); HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key) .referenceName(reference.name) .watchDurationMs(watchDurationMs) .gcDurationMs(gcDurationMs) .heapDumpDurationMs(heapDumpDurationMs) .build(); heapdumpListener.analyze(heapDump); } return DONE; }
我们看到,观察activity 就是 把 activity 放到WeakRefence 里面,WeakReference 的第二个参数是一个Qeue 对象,我们手动执行一下GC,然后Qeue.pull 一下,看看Qeue 里面是不是有WeakRefence ,如果有说明对象被回收了。那么这个activity 就没有泄漏,否则就有可能泄露了导致回收不了。
这个机制是WeakReference 的机制。有兴趣可以自行了解一下。
怎么执行GC?
Runtime.getRuntime().gc(); enqueueReferences(); System.runFinalization();
上面diam执行之后,我们再看下内存,还有没有activity 的实例,如果还有,那么说明这个activity 可能被泄露了。我们需要根据hprof 文件看下具体的调用链。
怎么生成hprof?
如果activity 内存泄露了,我们需要hprof 文件去分析。那么怎么拿到hprof?
Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
怎么分析hprof
使用了下面的开源项目:
implementation 'com.squareup.haha:haha:2.0.4'
分析完之后,用界面显示出来就没有什么好说了。
怎么在桌面有两个图标?
<activity android:label="@string/app_name" android:name=".MainActivity" > <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> <activity android:theme="@style/leak_canary_LeakCanary.Base" android:name=".internal.DisplayLeakActivity" android:process=":leakcanary" android:enabled="false" android:label="@string/leak_canary_display_activity_label" android:icon="@mipmap/leak_canary_icon" android:taskAffinity="com.squareup.leakcanary.${applicationId}" > <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity>
我们发现,leackCanary会生成一个自己的桌面图标,点击进去可以查看内漏泄露的信息,而且不需要创建桌面快捷方式的权限,就可以做到,他是用上面的方式做到的。清单里面可以有多个"android.intent.category.LAUNCHER",当然也会有一个默认的。
怎么设置组件启用和关闭
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); }
也就这样啦,没有具体怎么分析hprof,有兴趣可以再深入研究了。