Android开发LeakCanary源码解析

释放双眼,带上耳机,听听看~!

LeakCanary原理:watch一个即将要销毁的对象
1.在Activity 调用OnDestroy()之后将它放在一个WeakReference
2.将这个WeakReference关联到一个ReferenceQueue
3.查看ReferenceQueue是否存在Activity的引用
4.如果该Activit泄漏了,Dump出heap信息,然后再去分析泄漏路径

要了解内存泄漏首先要了解内存
内存分为:
1.栈(stack):存放基本类型和对象的引用
2.堆(heap):存放new出来,创建出来的对象或者数组,在堆中的内存是由Java虚拟机的垃圾回收器来管理的,JVM只有一个堆区,同时这个堆区会被所有线程所共享
3.方法区(method):方法区也可以称为静态区,被所有的线程共享的,方法区中还包含所有的class对象和静态变量

为什么会产生内存泄漏
1.对象不再需要使用,在对象需要被回收的时候,有其它正在使用的对象在引用它,导致这个本该被回收的对象不能被回收,本该被回收的对象还在堆内存中,就产生了内存泄漏
2.有些对象只有有限的生命周期,当这些生命周期很短的对象任务完成之后,就应该垃圾回收器回收,如果在这个对象的生命周期快被结束时,还被一系列的引用,就会导致内存泄漏

内存泄漏会导致什么问题
随着泄漏的不断累积,可能会消耗完整个内存,导致OOM

说到了引用,就说下引用类型
1.强引用(StrongReference):正常使用的对象就是强引用,强引用垃圾回收器是不会去回收强引用的对象,当内存不足,虚拟机宁愿抛出OOM的错误
2.软引用(SoftReference):内存空间足够的话,垃圾回收器不会去回收软引用的对象;但是内存空间不够的情况下,就回去回收软引用引用的对象的内存,只要垃圾回器没有回收这个软引用的对象,这个对象还是可以被使用的
3.弱引用(WeakReference):当垃圾回收器,扫描内存的时候,这时候不去区分内存够不够,会直接回收弱引用的对象
4.虚引用:形同虚设,不会决定一个对象的任何生命周期,垃圾回收器在任何情况下都可以回收

ReferenceQueue
软引用或者弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个引用加入到与之关联的引用队列中

看下LeakCanary.install方法

public static @NonNull RefWatcher install(@NonNull Application application) {//RefWatcher类是用于启动ActivityRefWatcher,然后通过ActivityRefWatcher去监视Activity的回收情况
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}

看下buildAndInstall()方法

  public RefWatcher buildAndInstall() {
RefWatcher refWatcher = build();//创建RefWatcher 对象
if (refWatcher != DISABLED) {
LeakCanary.enableDisplayLeakActivity(context);//LeakCanary就是使用的时候,弹框,告诉我们哪里有内存泄漏,enableDisplayLeakActivity开启Activity
ActivityRefWatcher.install((Application) context, refWatcher);
}
return refWatcher;
}

看下 ActivityRefWatcher.install(context, refWatcher)方法

  public static void install(Application application, RefWatcher refWatcher) {
new ActivityRefWatcher(application, refWatcher).watchActivities();
}
看下watchActivities()方法
```java
public void watchActivities() {
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);//重新注册activity的生命周期的Callback
}

看下stopWatchingActivities()方法

public void stopWatchingActivities() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);//反注册以前的Activity生命周期的Calback,也就是保证我们以前所做的内存泄漏的监听工作要删除
}
看下activityRefWatcher.lifecycleCallbacks源码
```java
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
};

看下ActivityLifecycleCallbacks

  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);//将Activity的生命周期关联到ActivityRefWatcher类当中
}
};

看下onActivityDestroyed(activity)方法

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

看下RefWatcher的成员变量

public final class RefWatcher {
public static final RefWatcher DISABLED = new RefWatcherBuilder<>().build();
private final WatchExecutor watchExecutor;//用于去执行内存泄漏的检测用的
private final DebuggerControl debuggerControl;//用于查询是否正在调试中,调试中就不会进行内存泄漏的检测判断
private final GcTrigger gcTrigger;//用于处理GC的,判断内存泄漏之前,给泄漏对象一次机会,先调用一次GC,如果不GC就会把Dumper文件显示出来
private final HeapDumper heapDumper;//Dumper出内存泄漏的堆文件
private final Set<String> retainedKeys;//持有那些待检测的,以及那些产生内存泄漏的引用的key
private final ReferenceQueue<Object> queue;//引用队列,主要是判断弱引用所持有的对象,是否已经被执行了GC垃圾回收
private final HeapDump.Listener heapdumpListener;//主要是用于分析一些产生Heap文件的回调
private final ExcludedRefs excludedRefs;//排除一些系统的bug引起的内存泄漏
.
.
.
}

看下 refWatcher.watch(activity)方法

 public void watch(Object watchedReference) {
watch(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();//对Refenerence添加一个唯一的key值引用
retainedKeys.add(key);//将唯一的key添加到前面说过的set集合当中
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);//创建所需要的弱引用对象
ensureGoneAsync(watchStartNanoTime, reference);//开启一个异步线程来分析,刚才所创建好的KeyedWeakReference
}

看下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);
}
});
}

看下ensureGone(reference, watchStartNanoTime)方法

Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {//用来确保Activity是否已经进入Gone状态,Gone表示Activity是否真的已经被回收了
long gcStartNanoTime = System.nanoTime();
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
removeWeaklyReachableReferences();
if (debuggerControl.isDebuggerAttached()) {//判断如果是出于处于调试状态的时候,就不会进行内存泄漏的分析
return RETRY;
}
if (gone(reference)) {//如果当前对象已经可达,已经不会造成内存泄漏,这就是不属于内存泄漏的范围
return DONE;
}
gcTrigger.runGc();//gcTrigger进行垃圾回收的对象,gcTrigger.runGc()如果当前对象没有改变它的可达状态,进行手动的垃圾回收
removeWeaklyReachableReferences();//清除已经到达引用队列的弱引用,把已经回收的对象的Key,从Set对象中移除,剩下的Key保证都是未被回收的
if (!gone(reference)) {//表示已经出现内存泄漏
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);//用于计算调用watch这个方法到我们调用了GC垃圾回收总共所产生的时间
File heapDumpFile = heapDumper.dumpHeap();//Dumper出我们所需要的内存泄漏文件
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
heapdumpListener.analyze( //  heapdumpListener.analyze去真正分析内存泄漏,以及它的路径
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
}
return DONE;
}

总结下:
1.首先会创建一个ActivityRefWatcher,启动一个ActivityRefWatcher
2.通过ActivityLifecycleCallbacks把activity的OnDestory生命周期关联
3.最后在线程池中去开始分析内存泄漏

看下 heapdumpListener.analyze( new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs, gcDurationMs, heapDumpDurationMs));这个抽象方法的实现

 @Override public void analyze(HeapDump heapDump) {
checkNotNull(heapDump, "heapDump");
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}

看下 HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);方法

public final class HeapAnalyzerService extends IntentService {//是个IntentService,最后会到 onHandleIntent(Intent intent)方法
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) {
Intent intent = new Intent(context, HeapAnalyzerService.class);
intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
intent.putExtra(HEAPDUMP_EXTRA, heapDump);
context.startService(intent);
}
public HeapAnalyzerService() {
super(HeapAnalyzerService.class.getSimpleName());
}
@Override protected void onHandleIntent(Intent intent) {
if (intent == null) {
CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
return;
}
String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);//获取runAnalysis(Context context, HeapDump heapDump, Class<? extends AbstractAnalysisResultService> listenerServiceClass)方法传进来的参数
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
HeapAnalyzer heapAnalyzer = new HeapAnalyzer(heapDump.excludedRefs);//用于分析堆内存的,heapDump.excludedRefs排除系统Bug所引起的内存泄漏
AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey);//分析内存使用的结果
AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);//回调进行结果的显示
}
}

看下heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey)方法

  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);
HprofParser parser = new HprofParser(buffer);//解析器
Snapshot snapshot = parser.parse();//具体的解析工作
deduplicateGcRoots(snapshot);//去重的GCRoot,就是会对分析的结果,去除重复的内存泄漏
Instance leakingRef = findLeakingReference(referenceKey, snapshot);//根据需要检测的类Key,来查询检测结果中是否我们需要的对象
// False alarm, weak reference was cleared in between key check and heap dump.
if (leakingRef == null) {//表示GC后,对象已经被清除了
return noLeak(since(analysisStartNanoTime));
}
return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef);//有泄漏,找出整个路径
} catch (Throwable e) {
return failure(e, since(analysisStartNanoTime));
}
}

总结 checkForLeak(最重要的方法)
1.把.hprof转化为Snapshot
2.优化GCRoots
3.找出泄漏的对象/找出泄漏对象的最短路径

看下findLeakingReference(referenceKey, snapshot)方法

private Instance findLeakingReference(String key, Snapshot snapshot) {
ClassObj refClass = snapshot.findClass(KeyedWeakReference.class.getName());//创建Class对象,通过KeyedWeakReference弱引用就可以查找出内存泄漏的对象
List<String> keysFound = new ArrayList<>();
for (Instance instance : refClass.getInstancesList()) {
List<ClassInstance.FieldValue> values = classInstanceValues(instance);
String keyCandidate = asString(fieldValue(values, "key"));
if (keyCandidate.equals(key)) {//找到了检测对象
return fieldValue(values, "referent");
}
keysFound.add(keyCandidate);//添加找到的值
}
throw new IllegalStateException(
"Could not find weak reference with key " + key + " in " + keysFound);
}

总结findLeakingReference方法
1.在Snapshot 快照中找到第一个弱引用(因为对象没有被回收,所以第一个对象就代表内存泄漏)
2.遍历这个对象的所有势力
3.如果Key值和最开始定义封装的Key值相同,那么返回这个泄漏对象

看下findLeakTrace(analysisStartNanoTime, snapshot, leakingRef)方法

private AnalysisResult findLeakTrace(long analysisStartNanoTime, Snapshot snapshot,
Instance leakingRef) {//根据泄漏对象的引用,找到的对象,找出最短的路径
ShortestPathFinder pathFinder = new ShortestPathFinder(excludedRefs);
ShortestPathFinder.Result result = pathFinder.findPath(snapshot, leakingRef);//这两行是判断内存泄漏的关键,通过分析hprof文件,找到内存泄漏的点,判断的依据就是GCRoot,GCRoot表示不能被垃圾回收器GC回收的对象。这里我们关注两种类型,第一种静态的;第二中被其它线程所引用,并且其它线程正在运行,没有结束
// False alarm, no strong reference path to GC Roots.
if (result.leakingNode == null) {
return noLeak(since(analysisStartNanoTime));
}
LeakTrace leakTrace = buildLeakTrace(result.leakingNode);//生成的内存泄漏的调用栈,如果发生内存泄漏,展示在你的屏幕上的就是这个LeakTrace
String className = leakingRef.getClassObj().getClassName();
// Side effect: computes retained size.
snapshot.computeDominators();
Instance leakingInstance = result.leakingNode.instance;
long retainedSize = leakingInstance.getTotalRetainedSize();//计算内存泄漏的空间大小
//  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));
}

总结findLeakTrace方法
1.解析Hprof文件,把和这个文件封装成Snopshot
2.根据弱引用和前面定义的Key值,确定泄漏的对象
3.找到最短的泄漏路径,作为结果反馈出来 (如果找不到,可能是误判或者被垃圾回收器回收了)

为TA充电
共{{data.count}}人
人已赞赏
Android文章

Android开发EventBus源码解析

2021-2-1 21:24:54

Android文章

Android开发Rxjava源码解析

2021-2-1 22:07:30

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索