前言
本篇LeakCanary
源码分析,基于1.6.3
版本,使用Java编写,而不是最新的2.x
版本。
2.x
版本使用Kotlin
语言重写,1.x
需要在代码中进行手动安装LeakCanary
,2.x
版本则是通过ContentProvider进行自动安装。
简单使用
- 添加依赖
dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.6.3'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3'
}
- 安装LeakCanary
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);
}
}
- 运行App,桌面会生成多一个
LeakCanary
的图标入口,当可能存在内存泄漏时,App页面会Toast提醒,并开始分析堆内存,分析完毕会发送一个通知,点击通知跳转到分析详情页,我们按照内存泄漏的链路,查找问题点修复即可,非常方便
源码分析
LeakCanary.install 安装
LeakCanary.install()
,是LeakCanary
的入口方法,我们从这个方法开始看起
- refWatcher(),创建
RefWatcherBuilder
,使用了构建者Builder模式 - listenerServiceClass(),设置展示分析结果的前台服务Class
- excludedRefs(),添加一些需要忽略的对象,例如
AOSP
中一些泄露的对象 - buildAndInstall(),开始安装,生成
RefWatcher
对象
public final class LeakCanary {
/**
* Creates a {@link RefWatcher} that works out of the box, and starts watching activity
* references (on ICS+).
*
* 安装LeakCanary
*/
public static @NonNull
RefWatcher install(@NonNull Application application) {
return refWatcher(application)
//设置分析内存泄露和发送结果通知的前台服务
.listenerServiceClass(DisplayLeakService.class)
//添加一些需要忽略的对象,例如AOSP中一些泄露的对象
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
//开始安装
.buildAndInstall();
}
/**
* 创建AndroidRefWatcherBuilder对象
*/
public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
return new AndroidRefWatcherBuilder(context);
}
}
- ActivityRefWatcher监听
Activity
的内存泄漏 - FragmentRefWatcher监听
Fragment
的内存泄漏
/** A {@link RefWatcherBuilder} with appropriate Android defaults. */
//AndroidRefWatcherBuilder继承于RefWatcherBuilder,是针对安卓平台使用的
public final class AndroidRefWatcherBuilder extends RefWatcherBuilder<AndroidRefWatcherBuilder> {
/**
* 默认内存泄漏监测的开始延时时间
*/
private static final long DEFAULT_WATCH_DELAY_MILLIS = SECONDS.toMillis(5);
/**
* Sets a custom {@link AbstractAnalysisResultService} to listen to analysis results. This
* overrides any call to {@link #heapDumpListener(HeapDump.Listener)}.
*
* 设置一个自定义的AbstractAnalysisResultService子类,用于分析内存泄露,并把结果发送到通知中
*/
public @NonNull AndroidRefWatcherBuilder listenerServiceClass(
@NonNull Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
enableDisplayLeakActivity = DisplayLeakService.class.isAssignableFrom(listenerServiceClass);
return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
}
/**
* 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() {
//不允许重复调用安装方法
if (LeakCanaryInternals.installedRefWatcher != null) {
throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
}
//创建RefWatcher对象
RefWatcher refWatcher = build();
//非禁用状态,进入if
if (refWatcher != DISABLED) {
if (enableDisplayLeakActivity) {
LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
}
//watchActivities 默认为true,默认允许监听Activity内存泄露
if (watchActivities) {
ActivityRefWatcher.install(context, refWatcher);
}
//watchFragments 默认为true,默认允许监听Fragment内存泄露
if (watchFragments) {
FragmentRefWatcher.Helper.install(context, refWatcher);
}
}
//更新安装完成的标志位
LeakCanaryInternals.installedRefWatcher = refWatcher;
return refWatcher;
}
}
}
ActivityRefWatcher
通过Application
注册Activity
生命周期回调,在Activity
的onDestroy()
生命周期方法回调时,通过RefWatcher
对Activity对象进行内存泄漏监测
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated
public final class ActivityRefWatcher {
public static void installOnIcsPlus(@NonNull Application application,
@NonNull RefWatcher refWatcher) {
install(application, refWatcher);
}
/**
* 开始监听Activity内存泄露
*/
public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {
Application application = (Application) context.getApplicationContext();
//创建监听对象
ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
//开始监听Activity生命周期
application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
}
/**
* Activity生命周期回调对象
*/
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityDestroyed(Activity activity) {
//Activity的onDestroy()被调用,准备销毁时,开始监听对象是否内存泄露
refWatcher.watch(activity);
}
};
private final Application application;
private final RefWatcher refWatcher;
/**
* Activity内存泄露监听器
*/
private ActivityRefWatcher(Application application, RefWatcher refWatcher) {
this.application = application;
this.refWatcher = refWatcher;
}
/**
* 开始监听
*/
public void watchActivities() {
// Make sure you don't get installed twice.
//确保只会有一次监听,避免重复注册
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
/**
* 停止监听
*/
public void stopWatchingActivities() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);
}
}
FragmentRefWatcher
FragmentRefWatcher也比较类型,先通过Application
监听Activity
的onCreate()
回调,在回调中获取FragmentManager
,注册Fragment
的生命周期
Fragment分android.app.Fragment
包下的Fragment,以及Support包的Fragment
,android.app.Fragment
包下的Fragment需要在Android 8.0
开始才有FragmentManager
监听Fragment
生命周期的方法,而Support
包的Fragment则直接提供了
/**
* Internal class used to watch for fragments leaks.
*/
public interface FragmentRefWatcher {
/**
* 监听Activity上Fragment的内存泄露
*/
void watchFragments(Activity activity);
final class Helper {
private static final String SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME =
"com.squareup.leakcanary.internal.SupportFragmentRefWatcher";
/**
* 开始监听Fragment内存泄露
*/
public static void install(Context context, RefWatcher refWatcher) {
List<FragmentRefWatcher> fragmentRefWatchers = new ArrayList<>();
//Android 8.0,监听android.app.Fragment包下的Fragment
if (SDK_INT >= O) {
fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher));
}
//监听Support包的Fragment,具体实现在SupportFragmentRefWatcher中
try {
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) {
}
//android.app.Fragment包、Support包的Fragment,都不能监听,那么return
if (fragmentRefWatchers.size() == 0) {
return;
}
Helper helper = new Helper(fragmentRefWatchers);
//注册监听Activity的生命周期
Application application = (Application) context.getApplicationContext();
application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks);
}
private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
//Activity销毁,监听Fragment内存泄露
for (FragmentRefWatcher watcher : fragmentRefWatchers) {
watcher.watchFragments(activity);
}
}
};
private final List<FragmentRefWatcher> fragmentRefWatchers;
private Helper(List<FragmentRefWatcher> fragmentRefWatchers) {
this.fragmentRefWatchers = fragmentRefWatchers;
}
}
}
AndroidOFragmentRefWatcher
/**
* Android 8.0以上,可监听android.app.Fragment包下的Fragment
*/
@RequiresApi(Build.VERSION_CODES.O) //
class AndroidOFragmentRefWatcher implements FragmentRefWatcher {
private final RefWatcher refWatcher;
AndroidOFragmentRefWatcher(RefWatcher refWatcher) {
this.refWatcher = refWatcher;
}
private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
new FragmentManager.FragmentLifecycleCallbacks() {
@Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment 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,注册Fragment的生命周期回调
FragmentManager fragmentManager = activity.getFragmentManager();
fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
}
}
SupportFragmentRefWatcher
/**
* 监听Support包的Fragment,该类会被反射创建和调用
*/
class SupportFragmentRefWatcher implements FragmentRefWatcher {
private final RefWatcher refWatcher;
SupportFragmentRefWatcher(RefWatcher refWatcher) {
this.refWatcher = refWatcher;
}
private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
new FragmentManager.FragmentLifecycleCallbacks() {
@Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment 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,注册Fragment的生命周期回调
if (activity instanceof FragmentActivity) {
FragmentManager supportFragmentManager =
((FragmentActivity) activity).getSupportFragmentManager();
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
}
}
}
RefWatcher
不难发现,不管是ActivityRefWatcher
,还是FragmentRefWatcher
,都是通过RefWatcher
的watch()
方法进行监听对象内存泄漏的,接下来就是分析它
- 首先,生成一个唯一Key,用于标识泄露的对象,并保存到一个Set集合
- 创建一个弱引用包裹需要监听内存泄漏的对象,并绑定queue弱引用队列,当弱引用被回收时,会把弱引用放进这个队列中
- 调用
ensureGoneAsync()
,开始定时监听对象是否被GC回收
public final class RefWatcher {
private final WatchExecutor watchExecutor;
private final DebuggerControl debuggerControl;
private final GcTrigger gcTrigger;
private final HeapDumper heapDumper;
private final HeapDump.Listener heapdumpListener;
private final HeapDump.Builder heapDumpBuilder;
private final Set<String> retainedKeys;
private final ReferenceQueue<Object> queue;
RefWatcher(WatchExecutor watchExecutor, DebuggerControl debuggerControl, GcTrigger gcTrigger,
HeapDumper heapDumper, HeapDump.Listener heapdumpListener, HeapDump.Builder heapDumpBuilder) {
this.watchExecutor = checkNotNull(watchExecutor, "watchExecutor");
this.debuggerControl = checkNotNull(debuggerControl, "debuggerControl");
this.gcTrigger = checkNotNull(gcTrigger, "gcTrigger");
this.heapDumper = checkNotNull(heapDumper, "heapDumper");
this.heapdumpListener = checkNotNull(heapdumpListener, "heapdumpListener");
this.heapDumpBuilder = heapDumpBuilder;
retainedKeys = new CopyOnWriteArraySet<>();
queue = new ReferenceQueue<>();
}
/**
* Identical to {@link #watch(Object, String)} with an empty string reference name.
*
* 和watch(object, string)方法相同,只是第二个参数 referenceName 为空字符串
*
* @see #watch(Object, String)
*/
public void watch(Object watchedReference) {
watch(watchedReference, "");
}
/**
* 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();
//生成一个唯一Key,用于标识泄露的对象
String key = UUID.randomUUID().toString();
//把唯一Key存起来
retainedKeys.add(key);
//创建一个弱引用,并绑定queue弱引用队列
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
//开始定时监听对象是否被GC回收
ensureGoneAsync(watchStartNanoTime, reference);
}
}
-
ensureGoneAsync()
,通过watchExecutor
对象,提交一个任务,任务调用ensureGone()
方法 - 这个
watchExecutor
是AndroidWatchExecutor
-
AndroidWatchExecutor
类似线程池,但它使用HandlerThread
来执行任务 - 先使用主线程的
Handler
,发送一个闲时消息,并在闲时消息中,再使用HandlerThread
发送一个5秒
延时的消息进行任务,接下来我们回到ensureGone()
public final class RefWatcher {
/**
* 开始定时监听对象是否被GC回收
* @param watchStartNanoTime 对象开始监听的时间
* @param reference 要监听的对象
*/
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
//通过线程池发出任务
watchExecutor.execute(new Retryable() {
@Override public Result run() {
//执行任务
return ensureGone(reference, watchStartNanoTime);
}
});
}
}
/**
* A {@link WatchExecutor} is in charge of executing a {@link Retryable} in the future, and retry
* later if needed.
*
* 对象监听任务执行器,还支持任务重试
*/
public interface WatchExecutor {
/**
* 不执行任务的实现
*/
WatchExecutor NONE = new WatchExecutor() {
@Override public void execute(Retryable retryable) {
}
};
/**
* 执行任务方法
*
* @param retryable 可重试任务
*/
void execute(Retryable retryable);
}
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 = new Handler(Looper.getMainLooper());
HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME);
handlerThread.start();
backgroundHandler = new Handler(handlerThread.getLooper());
this.initialDelayMillis = initialDelayMillis;
maxBackoffFactor = Long.MAX_VALUE / initialDelayMillis;
}
@Override public void execute(@NonNull Retryable retryable) {
//如果是主线程,发送闲时消息来处理任务
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
waitForIdle(retryable, 0);
} else {
//非主线程,把任务发到主线程,再重新执行 waitForIdle() 方法
postWaitForIdle(retryable, 0);
}
}
/**
* 发送闲时消息来执行任务
*
* @param retryable 任务
* @param failedAttempts 重试次数
*/
private void waitForIdle(final Retryable retryable, final int failedAttempts) {
// This needs to be called from the main thread.
//这个方法,必须在主线程调用
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override public boolean queueIdle() {
//闲时消息执行
postToBackgroundWithDelay(retryable, failedAttempts);
return false;
}
});
}
/**
* 确保在主线程中执行任务
*
* @param retryable 任务
* @param failedAttempts 重试次数
*/
private void postWaitForIdle(final Retryable retryable, final int failedAttempts) {
mainHandler.post(new Runnable() {
@Override public void run() {
waitForIdle(retryable, failedAttempts);
}
});
}
/**
* 延时指定时间再执行
*
* @param retryable 任务
* @param failedAttempts 重试次数
*/
private void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) {
long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor);
//计算延时时间
long delayMillis = initialDelayMillis * exponentialBackoffFactor;
backgroundHandler.postDelayed(new Runnable() {
@Override public void run() {
//执行任务
Retryable.Result result = retryable.run();
//如果任务结果是重试,那么再发一次闲时消息,再执行一次
if (result == RETRY) {
postWaitForIdle(retryable, failedAttempts + 1);
}
}
}, delayMillis);
}
}
- 任务执行,先调用
removeWeaklyReachableReferences()
,通过一个while循环,不断从队列中获取被回收的弱引用对象,如果能获取到,就是有对象被回调,那么把它从Set中移除 - 调用
gone()
方法,检查弱引用的Key是否还在Set中,如果不存在就代表已经被GC回收了,任务就结束了,如果还存在,那么继续 - 调用
gcTrigger
对象的runGc()
方法,该方法会通知JVM进行垃圾回收 - 通知GC完后,再次调用
removeWeaklyReachableReferences()
,清理已经被回收掉的弱引用对象的Key - 再次调用
gone()
方法,检查弱引用的Key是否已经被移除,如果被移除,证明没有内存泄漏,任务结束。 - 如果还是没有被移除,就是发生内存泄漏了,使用
HeapDump
对象把堆内存Dump下来进行分析
public final class RefWatcher {
@SuppressWarnings("ReferenceEquality") // Explicitly checking for named null.
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
//记录GC开始时间
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;
}
//通知一次GC
gcTrigger.runGc();
//再次移除已经被回收掉的弱引用对象
removeWeaklyReachableReferences();
//再查询一下,这个弱引用对象是否已被回收
if (!gone(reference)) {//还是没有被回收,可能是对象被内存泄露了
//记录Dump堆的时间
long startDumpHeap = System.nanoTime();
//计算出GC花费的时长
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
//执行Dump堆,生成.hprof文件
File heapDumpFile = heapDumper.dumpHeap();
//发现Dump失败了,那么再重试
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;
}
//Dump成功了,计算花费的时长
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;
}
/**
* 移除已经被回收掉的弱引用对象
*/
private void removeWeaklyReachableReferences() {
// 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;
//通过一个while循环,不断从队列中获取被回收的弱引用对象,如果能获取到,就是有对象被回调,那么把它从
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key);
}
}
/**
* 检查弱引用的Key是否还在Set中,如果不存在就代表已经被GC回收了
*
* @param reference 要被检查的对象
* @return true代表对象已被回收
*/
private boolean gone(KeyedWeakReference reference) {
return !retainedKeys.contains(reference.key);
}
}
GcTrigger
public interface GcTrigger {
/**
* 默认实现
*/
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 perform a gc.
//如果是 System.gc(),不会每次收到通知都执行GC,而 Runtime.getRuntime().gc(),更有可能执行GC
Runtime.getRuntime().gc();
//让线程睡眠一下
enqueueReferences();
//通知执行对象的 finalize() 方法
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();
}
}
};
/**
* 通知执行一次GC
*/
void runGc();
}
总结
LeakCanary
的内存泄漏监测方式,是通过对被观察对象通过一个WeakReference
弱引用进行包裹,并关联一个ReferenceQueue
弱引用队列来监听对象回收。
然后通过主线程的Handler
发送一个闲时消息,在闲时消息中,通过HandlerThread
,发送一个延时5秒
的消息,开始从弱引用队列中获取对象,如果能获取到对象,则证明被观察对象被回收了。
如果没有被回收,通知JVM进行GC,再检查一遍弱引用队列,如果获取能到对象,则已被回收,就是没有发生内存泄漏。但如果依旧没有获取到,则发生内存泄漏,开始把堆内存Dump下来,进行分析。
网友评论