Leakcanary(二)

public final RefWatcher build() {
if (isDisabled()) {
return RefWatcher.DISABLED;
}

if (heapDumpBuilder.excludedRefs == null) { // excludedRefs排除可以忽略的泄漏路径
heapDumpBuilder.excludedRefs(defaultExcludedRefs());
}

HeapDump.Listener heapDumpListener = this.heapDumpListener;// 解析完 hprof 文件并通知 DisplayLeakService 弹出提醒
if (heapDumpListener == null) {
heapDumpListener = defaultHeapDumpListener();
}

DebuggerControl debuggerControl = this.debuggerControl;//判断是否处于调试模式,调试模式中不会进行内存泄漏检测
if (debuggerControl == null) {
debuggerControl = defaultDebuggerControl();
}

HeapDumper heapDumper = this.heapDumper;// dump 内存泄漏处的 heap 信息,写入 hprof 文件
if (heapDumper == null) {
heapDumper = defaultHeapDumper();
}

WatchExecutor watchExecutor = this.watchExecutor; // 线程控制器,在 onDestroy() 之后并且主线程空闲时执行内存泄漏检测
if (watchExecutor == null) {
watchExecutor = defaultWatchExecutor();
}

GcTrigger gcTrigger = this.gcTrigger;//gcTrigger 用于 GC,watchExecutor 首次检测到可能的内存泄漏,会主动进行 GC,GC 之后会再检测一次,仍然泄漏的判定为内存泄漏,进行后续操作
if (gcTrigger == null) {
gcTrigger = defaultGcTrigger();
}

if (heapDumpBuilder.reachabilityInspectorClasses == null) {
heapDumpBuilder.reachabilityInspectorClasses(defaultReachabilityInspectorClasses());
}

return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,
heapDumpBuilder);
}

Install

public static void install(Context context, RefWatcher refWatcher) {
Application application = (Application) context.getApplicationContext();
ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);

application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
}//lifecycleCallbacks 监听了 Activity 的各个生命周期,在 onDestroy() 中开始检测当前 Activity 的引用。

@Override public void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}

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);//: 一个 Set 集合,每个检测的对象都对应着一个唯一的 key,存储在 retainedKeys 中
final KeyedWeakReference reference =// 自定义的弱引用,持有检测对象和对用的 key 值
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);
}
});
}//是一个异步任务,不会对主线程造成阻塞;

ensureGone

Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);

removeWeaklyReachableReferences();
//遍历引用队列 queue,判断队列中是否存在当前 Activity 的弱引用,存在则删除 retainedKeys 中对应的引用的 key值。

if (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks. 调试器可能直接产生内存泄露
return RETRY;
}
if (gone(reference)) {
return DONE;
}
//如果不包含,说明上一步操作中 retainedKeys 移除了该引用的 key 值,也就说上一步操作之前引用队列 queue 中包含该引用,GC 处理了该引用,未发生内存泄漏,返回 DONE,不再往下执行
//如果包含,并不会立即判定发生内存泄漏,可能存在某个对象已经不可达,但是尚未进入引用队列 queue。这时会主动执行一次 GC 操作之后再次进行判断。
gcTrigger.runGc();// 在进行一次gc
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);//进行泄漏分析(内存泄漏最短路径分析)
}
//如果不包含,说明上一步操作中 retainedKeys 移除了该引用的 key 值,也就说上一步操作之前引用队列 queue 中包含该引用,GC 处理了该引用,未发生内存泄漏,返回 DONE,不再往下执行
return DONE;
}

heapdumpListener.analyze

由ServiceHeapDumpListener所实现, 在方法HeapAnalyzerService.runAnalysis的startForegroundService中的onHandleIntentInForeground的checkForLeak

//在dump的堆信息中通过key查找出内存泄漏的弱引用对象,并且计算出最短的GC路径;
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);
//在快照中根据referenceKey查找是否有对应的内存泄漏对象
// False alarm, weak reference was cleared in between key check and heap dump.
if (leakingRef == null) {
return noLeak(since(analysisStartNanoTime)); //不存在内存泄漏的情况
}
//则进入下一步查找内存泄漏对象的GC最短路径
return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize);
} catch (Throwable e) {
return failure(e, since(analysisStartNanoTime));
}
}

findLeakTrace

进一步跟踪

private AnalysisResult findLeakTrace(long analysisStartNanoTime, Snapshot snapshot,
Instance leakingRef, boolean computeRetainedSize) {

listener.onProgressUpdate(FINDING_SHORTEST_PATH);
// 1. 寻找最短路径, 通过node.parent记忆当前的路径
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));
}

listener.onProgressUpdate(BUILDING_LEAK_TRACE);
LeakTrace leakTrace = buildLeakTrace(result.leakingNode);
// 2. 构建 LeakTrace

String className = leakingRef.getClassObj().getClassName();

// 计算3. retained size
long retainedSize;
if (computeRetainedSize) {

listener.onProgressUpdate(COMPUTING_DOMINATORS);
// Side effect: computes retained size.
snapshot.computeDominators();

Instance leakingInstance = result.leakingNode.instance;

retainedSize = leakingInstance.getTotalRetainedSize();

// TODO: check O sources and see what happened to android.graphics.Bitmap.mBuffer
if (SDK_INT <= N_MR1) {
listener.onProgressUpdate(COMPUTING_BITMAP_SIZE);
retainedSize += computeIgnoredBitmapRetainedSize(snapshot, leakingInstance);
}
} else {
retainedSize = AnalysisResult.RETAINED_HEAP_SKIPPED;
}

// 4. 构建最终的结果 AnalysisResult
return leakDetected(result.excludingKnownLeaks, className, leakTrace, retainedSize,
since(analysisStartNanoTime));
}

findPath

Result findPath(Snapshot snapshot, Instance leakingRef) {
//第一个参数是带有所有信息的snapshot对象,第二个参数是内存泄漏的那个类的封装对象
clearState();
canIgnoreStrings = !isString(leakingRef);

enqueueGcRoots(snapshot);
// 将上面找到的所有GC Roots添加到队列中

boolean excludingKnownLeaks = false;
LeakNode leakingNode = null;
// 如果将从GC Root开始的所有引用看做树,则这里就可以理解成使用广度优先搜索遍历引用“森林”
while (!toVisitQueue.isEmpty() || !toVisitIfNoPathQueue.isEmpty()) {
LeakNode node;
if (!toVisitQueue.isEmpty()) {
node = toVisitQueue.poll(); //继续遍历
} else {
// 如果toVisitQueue中没有元素,则取toVisitIfNoPathQueue中的元素
// 意思就是,如果遍历完了toVisitQueue还没有找到泄露的路径,那么就继续遍历设置了“例外”的那些对象
// “例外”是什么情况?在后续两个方法会讲到。
node = toVisitIfNoPathQueue.poll();
if (node.exclusion == null) {

throw new IllegalStateException("Expected node to have an exclusion " + node);
}
excludingKnownLeaks = true;
}

// Termination 终止
if (node.instance == leakingRef) {
leakingNode = node;
break;
}
// 因为一个对象可以被多个对象引用,以GC Root为根的引用树
// 并不是严格意义上的树,所以如果已经遍历过当前对象,就跳过
if (checkSeen(node)) {
continue;
}
// 下面是根据当前引用节点的类型,分别找到它们所引用的对象

if (node.instance instanceof RootObj) {
visitRootObj(node);
} else if (node.instance instanceof ClassObj) {
visitClassObj(node);
} else if (node.instance instanceof ClassInstance) {
visitClassInstance(node); // 点进去继续,还没完
} else if (node.instance instanceof ArrayInstance) {
visitArrayInstance(node);
} else {
throw new IllegalStateException("Unexpected type for " + node.instance);
}
}
return new Result(leakingNode, excludingKnownLeaks);
}

处理例外visitClassInstance

private void visitClassInstance(LeakNode node) {
ClassInstance classInstance = (ClassInstance) node.instance;
// 将设置了“例外”的对象记录下来
// 这里的“例外”就是上一个方法中提到的“例外”。是指那些低优先级的,或者说几乎不可能引发内存泄露的对象
// 例如SDK中的一些对象,诸如Message, InputMethodManager等,一般情况下,这些对象都不会导致内存泄露。
// 因此只有在遍历了其他对象之后,找不到泄露路径的情况下,才遍历这些对象。

Map<String, Exclusion> ignoredFields = new LinkedHashMap<>();
ClassObj superClassObj = classInstance.getClassObj();
Exclusion classExclusion = null;
while (superClassObj != null) {
Exclusion params = excludedRefs.classNames.get(superClassObj.getClassName());
if (params != null) {
// true overrides null or false.
// 如果当前类或者其父类被设置了“例外”,则将其赋值给classExclusion
if (classExclusion == null || !classExclusion.alwaysExclude) {
classExclusion = params;
}
}

Map<String, Exclusion> classIgnoredFields =
excludedRefs.fieldNameByClassName.get(superClassObj.getClassName());
if (classIgnoredFields != null) {
// 如果当前类及其父类包含例外的成员,将这些成员添加到ignoredFields中
ignoredFields.putAll(classIgnoredFields);
}
superClassObj = superClassObj.getSuperClassObj();
}

if (classExclusion != null && classExclusion.alwaysExclude) {
return;
}
// 遍历每一个成员

for (ClassInstance.FieldValue fieldValue : classInstance.getValues()) {
Exclusion fieldExclusion = classExclusion;

最后我还整理了很多Android中高级的PDF技术文档。以及一些大厂面试真题解析文档。

image

Android高级架构师之路很漫长,一起共勉吧!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
ClassInstance.FieldValue fieldValue : classInstance.getValues()) {
Exclusion fieldExclusion = classExclusion;

最后我还整理了很多Android中高级的PDF技术文档。以及一些大厂面试真题解析文档。

[外链图片转存中…(img-for66cys-1715906926998)]

Android高级架构师之路很漫长,一起共勉吧!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值