LeakCanary 的集成过程很简单,首先在 build.gradle 文件中添加依赖:
dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.1'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.1'
}
然后实现自己的 Application 类:
public class LeakcanaryApplication extends Application {
private static RefWatcher sRefWatcher;
@Override public void onCreate() {
super.onCreate();
Log.e("LeakcanaryApplication", "onCreate");
sRefWatcher = LeakCanary.install(this); //为了sRefWatcher一定要install
}
public static RefWatcher getsRefWatcher(){
return sRefWatcher;
}
}
在需要监控的Activity、Fragment、Service或要监控的特定对象等,
LeakcanaryApplication.getsRefWatcher().watch(this);
这样就集成完成了。当 LeakCanary 检测到内存泄露时,会自动弹出 Notification 通知开发者发生内存泄漏的 Activity 和引用链,以便进行修复。
public static RefWatcher install(Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
public static AndroidRefWatcherBuilder refWatcher(Context context) {
return new AndroidRefWatcherBuilder(context);
}
refWatcher() 方法新建了一个 AndroidRefWatcherBuilder 对象,该对象继承于 RefWatcherBuilder 类,配置了一些默认参数,利用建造者构建一个 RefWatcher 对象。
public AndroidRefWatcherBuilder listenerServiceClass(
Class extends AbstractAnalysisResultService> listenerServiceClass) {
return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
}
public final T heapDumpListener(HeapDump.Listener heapDumpListener) {
this.heapDumpListener = heapDumpListener;
return self();
}
==listenerServiceClass() 方法绑定了一个后台服务 DisplayLeakService,这个服务主要用来分析内存泄漏结果并发送通知==。可以自定义处理,继承DisplayLeakService实现自定义的监控处理Service。
excludedRefs() 方法定义了一些对于开发者可以忽略的路径,意思就是即使这里发生了内存泄漏,LeakCanary 也不会弹出通知。这大多是系统 Bug 导致的,无需用户进行处理。
最后调用 buildAndInstall() 方法构建 RefWatcher 实例并开始监听 Activity 的引用
要点分析:
install是有返回值的—RefWatcher,这个对象非常重要,LeakCanary就是通过这个对象进行Activity的内存监控
RefWatcher是通过Builder模式创建的;
install内部一连串的链式调用,最后调用了buildAndInstall()方法;
public RefWatcher buildAndInstall() {
if (LeakCanaryInternals.installedRefWatcher != null) {
throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
}
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
if (watchActivities) {
ActivityRefWatcher.install(context, refWatcher);
}
if (watchFragments) {
FragmentRefWatcher.Helper.install(context, refWatcher);
}
}
LeakCanaryInternals.installedRefWatcher = refWatcher;
return refWatcher;
}
watchActivities/watchFragments默认值都是true,所以ActivityRefWatcher和FragmentRefWatcher都将被启用;
ActivityRefWatcher用于监控Activity的内存泄漏;
FragmentRefWatcher用于监控Fragment的内存泄漏;
ActivityRefWatcher.install(context, refWatcher)方法
public static void install(Context context, RefWatcher refWatcher) {
Application application = (Application) context.getApplicationContext();
ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
}
要点分析:
1、registerActivityLifecycleCallbacks是Android Application的一个方法,注册了该方法,应用中每一个Activity的生命周期变化都会通过该方法回调回来;
2、registerActivityLifecycleCallbacks方法传入的参数是activityRefWatcher.lifecycleCallbacks,我们到ActivityRefWatcher中看下该方法的实现
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
};
ActivityRefWatcher只监听了onActivityDestroyed方法,也就是说每一个Activity调用onDestory的时候都会回调到onActivityDestroyed这个方法,通知LeakCanary该Activity应该被销毁
到这来我们知道了为什么只执行LeakCanary.install(this)一条语句就可以完成对整个app的Activity内存泄漏进行监听了.
接下来看看ActivityRefWatcher具体是如何监听内存泄漏的,RefWatcher有两个核心方法: watch() 和 ensureGone()
先看watch方法
public void watch(Object watchedReference) {
watch(watchedReference, "");
}
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);
}
==监听提供的引用,检查该引用是否可以被回收==。这个方法是非阻塞的,因为检测功能是在Executor中的异步线程执行的。
checkNotNull:分别检测watchedReference、referenceName是否为空,如果为空则抛出异常结束;
==随机生成一个key,该key用于唯一标识已产生内存泄漏的对象,或者准备检测的对象==;
创建KeyedWeakReference对象,并调用另一个核心方法ensureGone;
接着看另一个ensureGone方法:
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
@SuppressWarnings("ReferenceEquality") // Explicitly checking for named null.
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;
}
private boolean gone(KeyedWeakReference reference) {
return !retainedKeys.contains(reference.key);
}
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 ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key);
}
}
ensureGoneAsync() 里面会 调用watchExecutor.execute(),watchExecutor 的真正实现是 AndroidWatchExecutor,IdleHandler会==在主线程的空闲期得到执行==,当执行的时候会调用postToBackgroundWithDelay()。这个方法为 LeakCanary 检测的重试机制(创建dump文件或者toast显示超5秒等情况重试执行ensureGone)。
==这是一个异步任务,不会对主线程造成阻塞==。
removeWeaklyReachableReferences():==移除已经回收的弱引用对象的key。==
如果需要判断的对象已经销毁,则不继续执行。
如果==移除之后还是存在该引用的key则手动再GC一次==:gcTrigger.runGc()。
然后==二次调用removeWeaklyReachableReferences()再判断是否已经销毁==。
==如果二次确认还是存在则判断为内存泄漏了==,开始.hprof文件的dump。
调用heapdumpListener.analyze(heapDump)进行泄漏分析。
阶段总结一下:
==弱引用与ReferenceQueue联合使用,如果弱引用关联的对象被回收,则会把这个弱引用加入到ReferenceQueue中==;通过这个原理,可以看出removeWeaklyReachableReferences()执行后,会对应删除已被回收的对象的KeyedWeakReference的数据。==如果这个引用继续存在,那么就说明没有被回收==。
==为了确保最大保险的判定是否被回收,一共执行了两次回收判定,包括一次手动GC后的回收判定。两次都没有被回收,很大程度上说明了这个对象的内存被泄漏了,但并不能100%保证;因此LeakCanary是存在极小程度的误差的。==
hprof文件已经生成好了,接下来就是内存泄漏路径生成分析了,上一步heapdumpListener.analyze(heapDump)的调用。
@Override public void analyze(HeapDump heapDump) {
checkNotNull(heapDump, "heapDump");
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
要点分析:
public final class HeapAnalyzerService extends ForegroundService
implements AnalyzerProgressListener {
private static final String LISTENER_CLASS_EXTRA = "listener_class_extra";
private static final String HEAPDUMP_EXTRA = "heapdump_extra";
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);
ContextCompat.startForegroundService(context, intent);
}
public HeapAnalyzerService() {
super(HeapAnalyzerService.class.getSimpleName(), R.string.leak_canary_notification_analysing);
}
@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 =
new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses);
AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
heapDump.computeRetainedHeapSize);
AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
}
@Override public void onProgressUpdate(Step step) {
int percent = (int) ((100f * step.ordinal()) / Step.values().length);
CanaryLog.d("Analysis in progress, working on: %s", step.name());
String lowercase = step.name().replace("_", " ").toLowerCase();
String message = lowercase.substring(0, 1).toUpperCase() + lowercase.substring(1);
showForegroundNotification(100, percent, false, message);
}
}
要点分析:
HeapAnalyzer中核心的方法是checkForLeak,我们来看下它的具体实现:
/**
* Searches the heap dump for a {@link KeyedWeakReference} instance with the corresponding key,
* and then computes the shortest strong reference path from that instance to the GC roots.
*/
public AnalysisResult checkForLeak(File heapDumpFile, 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) {
return noLeak(since(analysisStartNanoTime));
}
return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize);
} catch (Throwable e) {
return failure(e, since(analysisStartNanoTime));
}
}
要点分析:
在dump的堆信息中通过key查找出内存泄漏的弱引用对象,并且计算出最短的GC路径;
Snapshot snapshot = parser.parse()–生成文件的快照
deduplicateGcRoots(snapshot)–过滤重复的内存泄漏对象;
findLeakingReference(referenceKey, snapshot)–==在快照中根据referenceKey查找是否有对应的内存泄漏对象==,如果获取到的leakingRef为空,则说明内存已经被回收了,不存在内存泄漏的情况;如果leakingRef不为空则进入下一步查找内存泄漏对象的GC最短路径;
findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize)–查找内存泄漏对象的GC最短路径;
LeakCanary的核心源码已经都已经分析完了,剩下的就是如果在页面上显示出来了。
==LeakCanary主要是通过Application的registerActivityLifecycleCallbacks方法监控每一个Activty的Destory之后对象是否被收回.==
==在Activity Destory之后,RefWatch的watch方法将被调用,watch方法会通过一个随机生成的key将这个弱引用关联到一个ReferenceQueue,然后调用ensureGone();==
(watch也可以传入其他需要监控的对象,将检测的对象放入弱引用中并且关联到引用队列中,查看引用队列中是否存在引用,如果发现泄露,dump出信息进行分析)
(当一个软引用/弱引用对象被垃圾回收后,Java虚拟机就会把这个引用加入到与之关联的引用队列中;)
(==判断有没被回收的依据==:弱引用与ReferenceQueue联合使用,如果弱引用关联的对象被回收,则会把这个弱引用加入到ReferenceQueue中,removeWeaklyReachableReferences()会清除掉retainedKeys中相应的key。在调用gone()方法时判断retainedKeys是否还包含相应的key,没包含说明回收了,包含说明没有回收)
一旦确认是内存泄漏,则开始dump信息到hprof文件中,并调用heapdumpListener.analyze(heapDump)开始内存分析;
==内存分析是在HeapAnalyzerService服务中进行的,属于一个单独的进程==;
HeapAnalyzerService的runAnalysis中创建HeapAnalyzer对象并调用它的一个核心方法checkForLeak();
==HeapAnalyzer的checkForLeak()会先解析hprof文件并生成快照文件,然后对快照中的泄漏对象进行去重,去重后根据第2步中的key去获取泄漏对象,如果对象为空则说明对象已经被回收,如果不为空则通过findLeakTrace()方法计算出最短GC路径==,并显示到DisplayLeakActivity页面,提醒开发者存在内存泄漏.
默认情况只对Activity和Fragment进行监控,也可以对其他模块的对象进行监控。
LeakcanaryApplication.getsRefWatcher().watch(this);
https://blog.csdn.net/hust_twj/article/details/90645228