从 LeakCanary.install(this);
开始
下面我们来看下它做了些什么
1、首先创建一个RefWatcher,启动一个ActivityRefWatcher
public static RefWatcher install(Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
这里主要是创建一个RefWatcher对象,其中有两个对象需要说明一下:
-
DisplayLeakService
:主要是用来展示内存泄漏的通知 -
AndroidExcludedRefs
:一个枚举类,里面定义了一下Android SDK内部的内存泄漏,当检测到系统的内存泄漏时会排除掉
再看一下buildAndInstall()
方法的内部实现
public RefWatcher buildAndInstall() {
//创建一个RefWatcher对象
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
//开启展示内存泄漏的activity
LeakCanary.enableDisplayLeakActivity(context);
// 开始检测Activity引用
ActivityRefWatcher.install((Application) context, refWatcher);
}
return refWatcher;
}
2、通过ActivityLifecycleCallbacks关联Activity的onDestory生命周期
Activity生命周期的关联是通过上一步中调用ActivityRefWatcher.install()
关联的,具体怎么实现的下面看一下源码:
public static void install(Application application, RefWatcher refWatcher) {
new ActivityRefWatcher(application, refWatcher).watchActivities();
}
//监控activity生命周期
public void watchActivities() {
// Make sure you don't get installed twice.
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
// activity生命周期回调
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);
}
};
3、在线程池中开启线程分析内存泄漏
Activity销毁后会调用RefWatcher
的watch
方法开始检测Activity
在看watch
的实现之前先卡一下RefWatcher
里面定义的变量,就能大概了解到RefWatcher主要做什么工作
public final class RefWatcher {
// 执行内存检测的线程池
private final WatchExecutor watchExecutor;
// 查询是否在debug调试中,如果再调试中不会执行内存泄漏的检测
private final DebuggerControl debuggerControl;
// 触发gc垃圾回收
private final GcTrigger gcTrigger;
// dump内存的堆文件
private final HeapDumper heapDumper;
// 持有待检测的和已经内存泄漏的对象的key
private final Set<String> retainedKeys;
// 创建WeakReference时传入的引用队列,主要是用来判断弱引用持有的对象是否被gc回收,gc回收后会把弱引用添加到队列中
private final ReferenceQueue<Object> queue;
// 监听产生heap文件的回调
private final HeapDump.Listener heapdumpListener;
// 需要排除的一些系统内存泄漏
private final ExcludedRefs excludedRefs;
}
看一下watch
的具体实现
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();
// 将要观察的类的key添加到set中
retainedKeys.add(key);
// 创建一个弱引用并将对象和key进行关联
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
ensureGoneAsync(watchStartNanoTime, reference);
}
// 开起线程检测对象是否被回收
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
4、判断对象是否被回收
判断一个对象是否被回收主要实在ensureGone
是完成的
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
// 删除已经被回收的弱引用
removeWeaklyReachableReferences();
// debug调试中返回
if (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks.
return RETRY;
}
// 已经回收,返回
if (gone(reference)) {
return DONE;
}
// 触发gc
gcTrigger.runGc();
removeWeaklyReachableReferences();
if (!gone(reference)) {
//dump内存快照
File heapDumpFile = heapDumper.dumpHeap();
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;
}
// 分析内存快照
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
}
return DONE;
}
private boolean gone(KeyedWeakReference reference) {
return !retainedKeys.contains(reference.key);
}
private void removeWeaklyReachableReferences() {
KeyedWeakReference ref;
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key);
}
}
5 checkforLeak
public AnalysisResult checkForLeak(File heapDumpFile, String referenceKey) {
long analysisStartNanoTime = System.nanoTime();
if (!heapDumpFile.exists()) {
Exception exception = new IllegalArgumentException("File does not exist: " + heapDumpFile);
return failure(exception, since(analysisStartNanoTime));
}
try {
HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
// 解析 hprof文件
HprofParser parser = new HprofParser(buffer);
// 转成 snapshot快照
Snapshot snapshot = parser.parse();
//去除重复的内存泄漏
deduplicateGcRoots(snapshot);
// 根据key查询解析结果是否有 我们需要的对象
Instance leakingRef = findLeakingReference(referenceKey, snapshot);
// 在heap dump过程中引用被回收
if (leakingRef == null) {
return noLeak(since(analysisStartNanoTime));
}
// 找出泄漏的路径
return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef);
} catch (Throwable e) {
return failure(e, since(analysisStartNanoTime));
}
}
- 把.hprof转为Snapshot
- 优化gcroots
- 找出泄漏的对象和泄漏对象的最短路径
6、findLeakingReference和 findLeakTrace
findLeakingReference
找出内存泄漏的引用
- 在Snapshot中找到弱引用
- 遍历KeyedWeakReference这个类的所有实例
- 如果key值和最开始定义的key值相同,那么返回这个泄漏对象
private Instance findLeakingReference(String key, Snapshot snapshot) {
//从内存快照找查找泄漏的弱引用对象
ClassObj refClass = snapshot.findClass(KeyedWeakReference.class.getName());
List<String> keysFound = new ArrayList<>();
for (Instance instance : refClass.getInstancesList()) {
List<ClassInstance.FieldValue> values = classInstanceValues(instance);
String keyCandidate = asString(fieldValue(values, "key"));
// key 值相等表示找到我们需要的内存泄漏的对象
if (keyCandidate.equals(key)) {
return fieldValue(values, "referent");
}
keysFound.add(keyCandidate);
}
}
findLeakTrace
找到最短路径作为反馈结果反馈出来
private AnalysisResult findLeakTrace(long analysisStartNanoTime, Snapshot snapshot,
Instance leakingRef) {
// 通过snapshot查找最短路径
ShortestPathFinder pathFinder = new ShortestPathFinder(excludedRefs);
ShortestPathFinder.Result result = pathFinder.findPath(snapshot, leakingRef);
// False alarm, no strong reference path to GC Roots.
if (result.leakingNode == null) {
return noLeak(since(analysisStartNanoTime));
}
//生成内存泄漏的调用栈
LeakTrace leakTrace = buildLeakTrace(result.leakingNode);
String className = leakingRef.getClassObj().getClassName();
// Side effect: computes retained size.
snapshot.computeDominators();
Instance leakingInstance = result.leakingNode.instance;
// 计算内存泄漏的空间大小
long retainedSize = leakingInstance.getTotalRetainedSize();
// TODO: check O sources and see what happened to android.graphics.Bitmap.mBuffer
if (SDK_INT <= N_MR1) {
retainedSize += computeIgnoredBitmapRetainedSize(snapshot, leakingInstance);
}
return leakDetected(result.excludingKnownLeaks, className, leakTrace, retainedSize,
since(analysisStartNanoTime));
}
6、如何dump内存快照
dump hprof文件
public File dumpHeap() {
......
//调用系统的dumpHprofData方法
Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
......
}
网友评论