本文我们来看下LeakCanary的源码,以下内容基于com.squareup.leakcanary:leakcanary-android:1.6.3
LeakCanary.install(this)
从install方法进入
public static @NonNull RefWatcher install(@NonNull Application application) {
return refWatcher(application)
.listenerServiceClass(DisplayLeakService.class) //用来在结束的时候发出一个通知
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build()) //过滤已知的泄漏,默认回传Android SDK已知的内存泄漏
.buildAndInstall(); //构建并启动LeakCanary
}
可以看到,这里会调用refWatcher方法,获取AndroidRefWatcherBuilder
对象
public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
return new AndroidRefWatcherBuilder(context);
}
接着,会通过建造者模式,创造出RefWatcher
对象,我们来看下其中的buildAndInstall()
public @NonNull RefWatcher buildAndInstall() {
if (LeakCanaryInternals.installedRefWatcher != null) {
throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
}
//调用build()创建出RefWatcher对象
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
if (enableDisplayLeakActivity) { //是否启用显示Leak的那个Activity,默认为true
//让LeakCanary的图标能在桌面够显示出来
LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
}
if (watchActivities) { //是否监控Activity,默认是true
ActivityRefWatcher.install(context, refWatcher);
}
if (watchFragments) { //是否监控Fragment,默认是true
FragmentRefWatcher.Helper.install(context, refWatcher);
}
}
LeakCanaryInternals.installedRefWatcher = refWatcher;
//返回refWatcher,对于其他对象,比如Service,大内存的对象,需要通过refWatcher去主动监控 (在onDestory方法里,调用refWatcher.watch())
return refWatcher;
}
我们先来看下ActivityRefWatcher.install
public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {
Application application = (Application) context.getApplicationContext();
ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
//注册registerActivityLifecycleCallbacks
application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
}
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityDestroyed(Activity activity) {
//当Activity Destory,调用refWatcher.watch
refWatcher.watch(activity);
}
};
可以看到,这里其实只是在Activity Destory的时候,去调用了refWatcher.watch。
refWatcher.watch干了什么呢? 这个放到后面再看。
我们先来看FragmentRefWatcher.Helper.install
public interface FragmentRefWatcher {
//抽象方法,用于决定support包还是Fragment自带的包
void watchFragments(Activity activity);
final class Helper {
private static final String SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME =
"com.squareup.leakcanary.internal.SupportFragmentRefWatcher";
public static void install(Context context, RefWatcher refWatcher) {
//创建FragmentRefWatchers集合
List<FragmentRefWatcher> fragmentRefWatchers = new ArrayList<>();
//Android版本是否达到了26
if (SDK_INT >= O) {
//用来观察自带部分的Fragment (就是不是support包下的Fragment),即API 26以下不支持自带Fragment的监控
//内部会调用FragmentManager.registerFragmentLifecycleCallbacks
fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher));
}
try {
//判断类名的方式,判断是否有依赖com.squareup.leakcanary:leakcanary-support-fragment包
Class<?> fragmentRefWatcherClass = Class.forName(SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME);
Constructor<?> constructor =
fragmentRefWatcherClass.getDeclaredConstructor(RefWatcher.class);
//如果有的话,用反射创造出Support包下的FragmentRefWatcher
FragmentRefWatcher supportFragmentRefWatcher =
(FragmentRefWatcher) constructor.newInstance(refWatcher);
fragmentRefWatchers.add(supportFragmentRefWatcher);
} catch (Exception ignored) {
}
//如果refwatchers为0,则直接返回
if (fragmentRefWatchers.size() == 0) {
return;
}
//如果有refWatcher,则创建Helper对象
Helper helper = new Helper(fragmentRefWatchers);
Application application = (Application) context.getApplicationContext();
//注册registerActivityLifecycleCallbacks - 这里在onActivityCreated的时候调用watchFragments进行观测,最终还是调用的FragmentManager.registerFragmentLifecycleCallbacks
application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks);
}
private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
for (FragmentRefWatcher watcher : fragmentRefWatchers) {
watcher.watchFragments(activity);
}
}
};
private final List<FragmentRefWatcher> fragmentRefWatchers;
private Helper(List<FragmentRefWatcher> fragmentRefWatchers) {
this.fragmentRefWatchers = fragmentRefWatchers;
}
}
}
接着来看下watch方法
public void watch(Object watchedReference, String referenceName) {
if (this == DISABLED) {
return;
}
checkNotNull(watchedReference, "watchedReference");
checkNotNull(referenceName, "referenceName");
//记录下watch开始时间
final long watchStartNanoTime = System.nanoTime();
//生成随机的key
String key = UUID.randomUUID().toString();
//将key添加到retainedKeys集合中
retainedKeys.add(key);
//创建KeyedWeakReference,它是WeakReference的子类
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
ensureGoneAsync(watchStartNanoTime, reference);
}
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
//这里watchExecutor实现类是AndroidWatchExecutor
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
来看AndroidWatchExecutor.execute方法
@Override public void execute(@NonNull Retryable retryable) {
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
waitForIdle(retryable, 0);
} else {
postWaitForIdle(retryable, 0);
}
}
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;
}
});
}
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.run()
//retryable是外部传入的参数,其最终调用的是ensureGone()
Retryable.Result result = retryable.run();
if (result == RETRY) {
postWaitForIdle(retryable, failedAttempts + 1);
}
}
}, delayMillis);
}
我们再来看下ensureGone()
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
//获得gc开始时间
long gcStartNanoTime = System.nanoTime();
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
//从queue中不断取出对象,移除retainedKeys中记录的相应的可以
//queue是会传入到KeyedWeakReference,用来记录是否已经被回收,能获取到表明已经被Activity回收掉了
removeWeaklyReachableReferences();
//是否处于调试状态
if (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks.
return RETRY;
}
//判断retainedKeys里是否还有reference.key(reference是否已被回收)
if (gone(reference)) {
return DONE;
}
//主动调用GC
gcTrigger.runGc();
//再次调用removeWeaklyReachableReferences
removeWeaklyReachableReferences();
//再来查看下检视对象reference有没有被回收
if (!gone(reference)) {
long startDumpHeap = System.nanoTime();
//记录GC消耗时间
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 heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
.referenceName(reference.name)
.watchDurationMs(watchDurationMs)
.gcDurationMs(gcDurationMs)
.heapDumpDurationMs(heapDumpDurationMs)
.build();
//在独立的进程里面进行堆文件的分析工作
heapdumpListener.analyze(heapDump);
}
return DONE;
}
来看下heapDumper.dumpHeap()
,heapDunmper是AndroidHeapDumper
@Override public @Nullable File newHeapDumpFile() {
//尝试通过listFiles遍历获得符合要求的文件
List<File> pendingHeapDumps = listFiles(new FilenameFilter() {
@Override public boolean accept(File dir, String filename) {
return filename.endsWith(PENDING_HEAPDUMP_SUFFIX);
}
});
// If a new heap dump file has been created recently and hasn't been processed yet, we skip.
// Otherwise we move forward and assume that the analyzer process crashes. The file will
// eventually be removed with heap dump file rotation.
for (File file : pendingHeapDumps) {
if (System.currentTimeMillis() - file.lastModified() < ANALYSIS_MAX_DURATION_MS) {
//是否有分析工作正在进行
CanaryLog.d("Could not dump heap, previous analysis still is in progress.");
return RETRY_LATER;
}
}
//找到.hprof文件,如果超过了7个,那么删除之前的
cleanupOldHeapDumps();
File storageDirectory = externalStorageDirectory();
if (!directoryWritableAfterMkdirs(storageDirectory)) {
if (!hasStoragePermission()) {
//没有权限,申请权限
CanaryLog.d("WRITE_EXTERNAL_STORAGE permission not granted");
requestWritePermissionNotification();
} else {
//如果可以写入,进行写入
String state = Environment.getExternalStorageState();
if (!Environment.MEDIA_MOUNTED.equals(state)) {
CanaryLog.d("External storage not mounted, state: %s", state);
} else {
CanaryLog.d("Could not create heap dump directory in external storage: [%s]",
storageDirectory.getAbsolutePath());
}
}
//尝试用手机app自带的空间进行存储
// Fallback to app storage.
storageDirectory = appStorageDirectory();
if (!directoryWritableAfterMkdirs(storageDirectory)) {
//如果也不能写入,就只能重试
CanaryLog.d("Could not create heap dump directory in app storage: [%s]",
storageDirectory.getAbsolutePath());
return RETRY_LATER;
}
}
// If two processes from the same app get to this step at the same time, they could both
// create a heap dump. This is an edge case we ignore.
//创建File对象,_pending.horef的后缀
return new File(storageDirectory, UUID.randomUUID().toString() + PENDING_HEAPDUMP_SUFFIX);
}
public File dumpHeap() {
//通过Provider获取File (创建空的file对象)
File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();
if (heapDumpFile == RETRY_LATER) {
return RETRY_LATER;
}
FutureResult<Toast> waitingForToast = new FutureResult<>();
showToast(waitingForToast);
if (!waitingForToast.wait(5, SECONDS)) {
CanaryLog.d("Did not dump heap, too much time waiting for Toast.");
return RETRY_LATER;
}
//构建Notification
Notification.Builder builder = new Notification.Builder(context)
.setContentTitle(context.getString(R.string.leak_canary_notification_dumping));
Notification notification = LeakCanaryInternals.buildNotification(context, builder);
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
int notificationId = (int) SystemClock.uptimeMillis();
notificationManager.notify(notificationId, notification);
Toast toast = waitingForToast.get();
try {
//>>>>>>>> 得到堆信息 (Android平台提供的方法) <<<<<<<<<<<
Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
cancelToast(toast);
notificationManager.cancel(notificationId);
return heapDumpFile;
} catch (Exception e) {
CanaryLog.d(e, "Could not dump heap");
// Abort heap dump
return RETRY_LATER;
}
}
我们再来看下,是怎么分析堆信息的heapdumpListener.analyze(heapDump);
public static void runAnalysis(Context context, HeapDump heapDump,
Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
setEnabledBlocking(context, HeapAnalyzerService.class, true);
setEnabledBlocking(context, listenerServiceClass, true);
Intent intent = new Intent(context, HeapAnalyzerService.class);
intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
intent.putExtra(HEAPDUMP_EXTRA, heapDump);
//启动Service
ContextCompat.startForegroundService(context, intent);
}
这个Service是HeapAnalyzerService,在独立进程中运行
来看HeapAnalyzerService的onHandleIntentInForeground方法
@Override protected void onHandleIntentInForeground(@Nullable Intent intent) {
if (intent == null) {
CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
return;
}
String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
//创建HeapAnalyzer对象
HeapAnalyzer heapAnalyzer =
new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses);
//使用checkForLeak来进行堆内存的分析
AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
heapDump.computeRetainedHeapSize);
//将结果展示出来
AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
}
看下checkForLeak
,看它是怎么进行分析的
public @NonNull AnalysisResult checkForLeak(@NonNull File heapDumpFile,
@NonNull String referenceKey,
boolean computeRetainedSize) {
long analysisStartNanoTime = System.nanoTime();
if (!heapDumpFile.exists()) {
Exception exception = new IllegalArgumentException("File does not exist: " + heapDumpFile);
return failure(exception, since(analysisStartNanoTime));
}
try {
listener.onProgressUpdate(READING_HEAP_DUMP_FILE);
HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
HprofParser parser = new HprofParser(buffer);
listener.onProgressUpdate(PARSING_HEAP_DUMP);
Snapshot snapshot = parser.parse();
listener.onProgressUpdate(DEDUPLICATING_GC_ROOTS);
deduplicateGcRoots(snapshot);
listener.onProgressUpdate(FINDING_LEAKING_REF);
//从堆文件中找到我们的弱应用对象
Instance leakingRef = findLeakingReference(referenceKey, snapshot);
// False alarm, weak reference was cleared in between key check and heap dump.
//如果没有找到,说明没有内存泄漏
if (leakingRef == null) {
String className = leakingRef.getClassObj().getClassName();
return noLeak(className, since(analysisStartNanoTime));
}
//内部会使用一个第三方库,seuareup.haha来进行解析,其主要原理就是通过 hrpof 文件协议进行解析
return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize);
} catch (Throwable e) {
return failure(e, since(analysisStartNanoTime));
}
}
可以看到,内部调用了findLeakTrace,内部用的是haha这个库,其主要原理就是通过 hrpof 文件协议进行解析。
这里就不再继续分析下去了。因为这里到了另一个大的话题,需要虚拟机相关的知识,尤其是 heap 管理这一块,以及需要熟知 hprof 协议。这就不在本文的范畴里了,haha的原理可以去看haha库相关的文章。
.hprof文件存储的目录可以在LOG中查看到
LeakCanary的原理就是使用 RefWatcher 监测 Activity/Fragment 组件的生命周期,组件在进入 onDestroy 后,一般 framework 会主动 gc 一次,如果没有则还会强制 gc 一次。同时,还利用了弱引用在一次 gc 后便会进入到内存回收的状态的原理从而判断当前被监测的组件是否发生了内存泄漏。然后在HeapAnalyzerService(独立进程)中,使用HeapAnalyzer分析hprof,内部其实调用的是haha库来分析hprof文件。
现在LeakCanary已经更新到了2.0,2.0对比1.x有什么区别呢 ?
主要有以下区别
com.seuareup.leakcanary:shark
,宣称减少90%内存占用,速度提升6倍