LeakCanary源码分析
Posted 低调小一
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeakCanary源码分析相关的知识,希望对你有一定的参考价值。
基本使用
LeakCanary是用来检测android内存泄漏的工具。
在gradlew文件中引入:
dependencies
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.4'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4'
在项目Application中增加如下代码:
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;
LeakCanary.install(this);
// Normal app init code...
源码分析
从LeakCanary使用示例可以看出,在项目的Application中只要增加一行LeakCanary.install(this)即可检测内存泄漏。那我们就从这行代码开始深入分析。
install()
/**
* Creates a @link RefWatcher that works out of the box, and starts watching activity
* references (on ICS+).
*/
public static RefWatcher install(Application application)
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
/** Builder to create a customized @link RefWatcher with appropriate Android defaults. */
public static AndroidRefWatcherBuilder refWatcher(Context context)
return new AndroidRefWatcherBuilder(context);
install()方法里面首先创建了一个AndroidRefWatcherBuilder类。从名字就可以看出,这里使用了建造者模式。
DisplayLeakService.java
public class DisplayLeakService extends AbstractAnalysisResultService
@Override protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result)
// ......
private boolean saveResult(HeapDump heapDump, AnalysisResult result)
// ......
private HeapDump renameHeapdump(HeapDump heapDump)
// ......
protected void afterDefaultHandling(HeapDump heapDump, AnalysisResult result, String leakInfo)
DisplayLeakService本身继承自AbstractAnalysisResultService,而AbstractAnalysisResultService是一个IntentService.
public abstract class AbstractAnalysisResultService extends IntentService
private static final String HEAP_DUMP_EXTRA = "heap_dump_extra";
private static final String RESULT_EXTRA = "result_extra";
/**
* 静态方法,便于调用
*/
public static void sendResultToListener(Context context, String listenerServiceClassName,
HeapDump heapDump, AnalysisResult result)
Class<?> listenerServiceClass;
try
listenerServiceClass = Class.forName(listenerServiceClassName);
catch (ClassNotFoundException e)
throw new RuntimeException(e);
Intent intent = new Intent(context, listenerServiceClass);
intent.putExtra(HEAP_DUMP_EXTRA, heapDump);
intent.putExtra(RESULT_EXTRA, result);
context.startService(intent);
public AbstractAnalysisResultService()
super(AbstractAnalysisResultService.class.getName());
@Override protected final void onHandleIntent(Intent intent)
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAP_DUMP_EXTRA);
AnalysisResult result = (AnalysisResult) intent.getSerializableExtra(RESULT_EXTRA);
try
onHeapAnalyzed(heapDump, result);
finally
//noinspection ResultOfMethodCallIgnored
heapDump.heapDumpFile.delete();
protected abstract void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result);
DisplayLeakService继承自IntentService,将分析Heap文件这种耗时操作放在子线程中执行,然后存储日志记录,发送Notification通知,避免出现ANR。
AndroidExcludedRefs.java
public enum AndroidExcludedRefs
// ######## Android SDK Excluded refs ########
AndroidExcludedRef是一个枚举类,用来定义白名单。在白名单里面的泄漏case,不会被记录,也不会通过DisplayLeakService通知给用户。如果我们发现有系统的内存泄漏,当前是无法解决的,就可以继承这个类,增加相应的白名单,避免开发时不必要的内存泄漏上报。
buildAndInstall()
public RefWatcher buildAndInstall()
RefWatcher refWatcher = build();
if (refWatcher != DISABLED)
LeakCanary.enableDisplayLeakActivity(context);
ActivityRefWatcher.install((Application) context, refWatcher);
return refWatcher;
首先,通过建造者方法创建出RefWatcher类。如果创建出来的refWatcher不等于DISABLE,说明当前线程是主线程,不是内存泄漏分析线程,那就接着进行初始化操作。
LeakCanary.enableDisplayLeakActivity
public static void enableDisplayLeakActivity(Context context)
setEnabled(context, DisplayLeakActivity.class, true);
public static void setEnabled(Context context, final Class<?> componentClass,
final boolean enabled)
final Context appContext = context.getApplicationContext();
executeOnFileIoThread(new Runnable()
@Override public 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);
通过源码可以看出,这里是使用PackageManager来使得DisplayLeakActivity处于enable状态。之所以需要这样做,是因为LeakCanary在最初始是将DisplayLeakActivity设置为disable的,这样桌面上就不会显示LeakCanary的桌面图标。
<activity
android:theme="@style/leak_canary_LeakCanary.Base"
android:name=".internal.DisplayLeakActivity"
android:enabled="false"
android:label="@string/leak_canary_display_activity_label"
android:icon="@drawable/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>
ActivityRefWatcher.install
public final class ActivityRefWatcher
public static void install(Application application, RefWatcher refWatcher)
new ActivityRefWatcher(application, refWatcher).watchActivities();
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new Application.ActivityLifecycleCallbacks()
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState)
@Override public void onActivityStarted(Activity activity)
@Override public void onActivityResumed(Activity activity)
@Override public void onActivityPaused(Activity activity)
@Override public void onActivityStopped(Activity activity)
@Override public void onActivitySaveInstanceState(Activity activity, Bundle outState)
@Override public void onActivityDestroyed(Activity activity)
ActivityRefWatcher.this.onActivityDestroyed(activity);
;
private final Application application;
private final RefWatcher refWatcher;
/**
* Constructs an @link ActivityRefWatcher that will make sure the activities are not leaking
* after they have been destroyed.
*/
public ActivityRefWatcher(Application application, RefWatcher refWatcher)
this.application = checkNotNull(application, "application");
this.refWatcher = checkNotNull(refWatcher, "refWatcher");
void onActivityDestroyed(Activity activity)
refWatcher.watch(activity);
public void watchActivities()
// Make sure you don't get installed twice.
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
public void stopWatchingActivities()
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);
通过源码可以看出,使用Application.ActivityLifecycleCallbacks来监听Activity的生命周期。为了保证不初始化两次监听,先移除了一次,然后再次添加监听。每次当Activity执行完onDestroy生命周期,LeakCanary就会获取到这个Activity,然后对它进行分析,查看是否存在内存泄漏。
RefWatcher类
成员变量
public final class RefWatcher
// 不在主线程时,用于标记的RefWatcher
public static final RefWatcher DISABLED = new RefWatcherBuilder<>().build();
// 主线程执行任务池
private final WatchExecutor watchExecutor;
// Debug控制器
private final DebuggerControl debuggerControl;
// 内部调用Runtime.getRuntime().gc(),手动触发系统GC
private final GcTrigger gcTrigger;
// 用于创建.hprof文件,用于储存heap堆的快照
private final HeapDumper heapDumper;
// 内存中没被gc的对象key集合
private final Set<String> retainedKeys;
// 与WeakReference配合使用的ReferenceQueue
private final ReferenceQueue<Object> queue;
// 监听heap分析
private final HeapDump.Listener heapdumpListener;
// 内存忽略白名单
private final ExcludedRefs excludedRefs;
watch方法
在ActivityRefWatcher的Application.ActivityLifecycleCallbacks中,我们为每个Activity的onDestory方法增加了监听,在onDestory方法中调用如下代码:
ActivityRefWatcher.this.onActivityDestroyed(activity);
而ActivityRefWatcher的onActivityDestoryed方法实际调用的是RefWatcher的watch方法:
void onActivityDestroyed(Activity activity)
refWatcher.watch(activity);
RefWatcher的watch方法源码如下:
public void watch(Object watchedReference)
watch(watchedReference, "");
public void watch(Object watchedReference, String referenceName)
if (this == DISABLED)
return;
// 检测watchedReference是否为null
checkNotNull(watchedReference, "watchedReference");
// 检测referenceName是否为null
checkNotNull(referenceName, "referenceName");
final long watchStartNanoTime = System.nanoTime();
// 生成对应watchReference的唯一标识,并添加到retainedKeys集合中
String key = UUID.randomUUID().toString();
retainedKeys.add(key);
// 构造WeakReference,这里的WeakReference是集合queue一起使用的
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
// 检测对象是否内存泄漏
ensureGoneAsync(watchStartNanoTime, reference);
ensureGoneAsync
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference)
watchExecutor.execute(new Retryable()
@Override public Retryable.Result run()
return ensureGone(reference, watchStartNanoTime);
);
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime)
long gcStartNanoTime = System.nanoTime();
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
// 移除掉已经回收的对象的key
removeWeaklyReachableReferences();
// 如果retainedKeys已经不包含这个reference,说明对象已经回收了
if (gone(reference))
return DONE;
// 手动触发一次系统GC
gcTrigger.runGc();
// 再次移除GC后已经回收的对象的key
removeWeaklyReachableReferences();
if (!gone(reference))
// 如果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);
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
return DONE;
首先执行removeWeaklyReachableReferences()方法尝试着从弱引用的队列中获取待分析对象,如果不为空,说明该对象已经被系统回收了,就将retainedKeys中对应的key去掉。如果被系统回收了,直接就返回DONE;如果没有被系统回收,可能存在内存泄漏,为了保证结果的准确性,还需要调用gcTrigger.runGc(),手动触发一下系统GC,然后再尝试移除待分析对象,如果还存在,说明存在内存泄漏。
gcTrigger.runGc
我们看一下LeakCanary是如何触发GC的:
GcTrigger DEFAULT = new GcTrigger()
@Override public 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();
private void enqueueReferences()
// Hack. We don't have a programmatic way to wait for the reference queue daemon to move
// references to the appropriate queues.
try
Thread.sleep(100);
catch (InterruptedException e)
throw new AssertionError();
;
与使用System.gc()不同,这里调用的是Runtime.getRuntime().gc()方法,enqueueReference()方法通过沉睡100毫秒给系统GC时间,System.runFinalization()是强制调用已经失去引用的对象的finalize方法。
总结
通过对LeakCanary的源码分析,我们可以知道,LeakCanary检测内存泄漏分为三个步骤,分别是:
监听
在Android中,当一个Activity走完onDestroy生命周期后,说明该页面已经被销毁了,应该被系统GC回收。通过Application.registerActivityLifecycleCallbacks()方法注册Activity生命周期的监听,每当一个Activity页面销毁时候,通过在onDestory生命周期回调里获取到这个Activity,并去检测这个Activity是否真的被系统GC。
检测
通过WeakReference + ReferenceQueue来判断对象是否被系统GC回收,WeakReference 创建时,可以传入一个 ReferenceQueue 对象。当被 WeakReference 引用的对象的生命周期结束,一旦被 GC 检查到,GC 将会把该对象添加到 ReferenceQueue 中,待ReferenceQueue处理。当 GC 过后对象一直不被加入 ReferenceQueue,它可能存在内存泄漏。当我们初步确定待分析对象未被GC回收时候,手动触发GC,二次确认。
分析
分析使用了Square的开源库haha,利用它获取当前内存中的heap堆的快照t,然后通过待分析对象去snapshot里面去查找最短路径,并通过系统通知告知用户。
以上是关于LeakCanary源码分析的主要内容,如果未能解决你的问题,请参考以下文章
全新 LeakCanary 2 ! 完全基于 Kotlin 重构升级 !