本文共 10833 字,大约阅读时间需要 36 分钟。
最近高产似母猪,闲下来的时候就喜欢找找源码看。昨天看了下LeakCanary,准备来分析一波。
gradle文件中添加:
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4' // debug releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4' // release
Application中通过LeakCanary.install(this);
进行初始化。
public static RefWatcher install(Application application) { return refWatcher(application).listenerServiceClass(DisplayLeakService.class) .excludedRefs(AndroidExcludedRefs.createAppDefaults().build()) .buildAndInstall();}// 创建AndroidRefWatcherBuilder对象public static AndroidRefWatcherBuilder refWatcher(Context context) { return new AndroidRefWatcherBuilder(context);}// 只是保存applicationContextAndroidRefWatcherBuilder(Context context) { this.context = context.getApplicationContext();}// 服务监听public AndroidRefWatcherBuilder listenerServiceClass( Class listenerServiceClass) { return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));}// 堆内存镜像监听服务public final class ServiceHeapDumpListener implements HeapDump.Listener { private final Context context; private final Class listenerServiceClass; public ServiceHeapDumpListener(Context context, Class listenerServiceClass) { setEnabled(context, listenerServiceClass, true); setEnabled(context, HeapAnalyzerService.class, true); this.listenerServiceClass = checkNotNull(listenerServiceClass, "listenerServiceClass"); this.context = checkNotNull(context, "context").getApplicationContext(); } @Override public void analyze(HeapDump heapDump) { checkNotNull(heapDump, "heapDump"); HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass); }}
这里有两个重点:
AndroidRefWatcherBuilder
保存了ServiceHeapDumpListener
监听器。ServiceHeapDumpListener
监听的服务是DisplayLeakService
在初始化时,AndroidExcludedRefs.createAppDefaults().build()
创建了默认忽略内存泄漏的属性,这里不多说。
多说一句,这里面有个代码是setEnabled
,其最终实现是:
public static void setEnabledBlocking(Context appContext, Class componentClass, boolean enabled) { ComponentName component = new ComponentName(appContext, componentClass); PackageManager packageManager = appContext.getPackageManager(); int newState = enabled ? COMPONENT_ENABLED_STATE_ENABLED : COMPONENT_ENABLED_STATE_DISABLED; // Blocks on IPC. packageManager.setComponentEnabledSetting(component, newState, DONT_KILL_APP);}
这个主要就是设置组件可用状态,因为这里面默认的许多组件都是不可用状态,下面的Service以及Activity的enabled属性都为false:
buildAndInstall()
方法: public RefWatcher buildAndInstall() { RefWatcher refWatcher = build(); if (refWatcher != DISABLED) { LeakCanary.enableDisplayLeakActivity(context); // 将Application和ActivityRefWatcher进行绑定 ActivityRefWatcher.install((Application) context, refWatcher); } return refWatcher;}public static void install(Application application, RefWatcher refWatcher) { new ActivityRefWatcher(application, refWatcher).watchActivities();}private final Application.ActivityLifecycleCallbacks lifecycleCallbacks = new Application.ActivityLifecycleCallbacks() { // 省略其他生命周期,只有在onActivityDestroyed会调用 ...... @Override public void onActivityDestroyed(Activity activity) { ActivityRefWatcher.this.onActivityDestroyed(activity); } };// 注册对Activity的监听public void watchActivities() { // Make sure you don't get installed twice. stopWatchingActivities(); application.registerActivityLifecycleCallbacks(lifecycleCallbacks);}
到这里就知道LeakCanary是如何监听页面销毁的了,和Lifecycle一样,通过注册Application.ActivityLifecycleCallbacks
实现对Activity生命周期的监听,在destory的时候进行内存分析。
从上面看到,当Activity执行了onDestory生命周期后,就会触发ActivityRefWatcher.this.onActivityDestroyed(activity)
,这里有几个方法调用:
void onActivityDestroyed(Activity activity) { refWatcher.watch(activity);}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(); // 将随机生成的key保存 retainedKeys.add(key); // 创建了一个存储了随机生成的key的弱引用,并且传入了ReferenceQueue 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); } });}
这里将我们传入的Activity通过弱引用的方式保存起来KeyedWeakReference
,并且还将随机生成一个key作为改Activity的唯一标志。
AndroidWatchExecutor
,AndroidWatchExecutor
根据返回的结果Retryable.Result
进行判断,如果为DONE
,则代表处理完成;如果为RETRY
,则需要重新处理。 ensureGone
的操作吧: Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) { long gcStartNanoTime = System.nanoTime(); long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime); // 移除掉已经被释放的Activity对应的key removeWeaklyReachableReferences(); // debug状态 if (debuggerControl.isDebuggerAttached()) { // The debugger can create false leaks. return RETRY; } // 判断是否结束 if (gone(reference)) { return DONE; } // 手动调用gc,这里会使线程休眠100ms gcTrigger.runGc(); // 再次移除已经被释放的Activity对应的key removeWeaklyReachableReferences(); // 如果还是存在key,说明有内存泄漏 if (!gone(reference)) { long startDumpHeap = System.nanoTime(); long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime); // 获取heapDumpFile文件,这里heapDumper是AndroidHeapDumper,会弹出那个经典的toast File heapDumpFile = heapDumper.dumpHeap(); if (heapDumpFile == RETRY_LATER) { // Could not dump the heap. return RETRY; } long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap); // 调用ServiceHeapDumpListener的analyze方法 heapdumpListener.analyze( new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs, gcDurationMs, heapDumpDurationMs)); } return DONE;}private void removeWeaklyReachableReferences() { // 弱引用对应的资源被释放,会将弱引用放到Quene中,通过poll可以出队列 KeyedWeakReference ref; while ((ref = (KeyedWeakReference) queue.poll()) != null) { // 将KeyedWeakReference对应的key移除 retainedKeys.remove(ref.key); }}// 是否结束的标志是保存的key中是否存在当前弱引用的keyprivate boolean gone(KeyedWeakReference reference) { return !retainedKeys.contains(reference.key);}
流程如下:
AndroidHeapDumper
生成文件,并且调用之前传入的ServiceHeapDumpListener.analyze()
方法ServiceHeapDumpListener
的analyze()
方法:
@Override public void analyze(HeapDump heapDump) { checkNotNull(heapDump, "heapDump"); HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);}
其中listenerServiceClass
是我们创建时添加的类,其类为DisplayLeakService
(后面会用到)。
runAnalysis
方法: public static void runAnalysis(Context context, HeapDump heapDump, Class listenerServiceClass) { Intent intent = new Intent(context, HeapAnalyzerService.class); intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName()); intent.putExtra(HEAPDUMP_EXTRA, heapDump); context.startService(intent);}
该方法只是启动了一个继承了IntentService
的HeapAnalyzerService
,可以看下其onHandleIntent
方法:
@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); HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA); HeapAnalyzer heapAnalyzer = new HeapAnalyzer(heapDump.excludedRefs); // 分析过程 AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey); AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);}
这里面主要的分析过程时通过HeapAnalyzer的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 { // 将保存的hprof文件转为Snapshot对象,这里通过com.squareup.haha实现 HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile); HprofParser parser = new HprofParser(buffer); Snapshot snapshot = parser.parse(); deduplicateGcRoots(snapshot); // 查找泄漏的引用 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); } catch (Throwable e) { return failure(e, since(analysisStartNanoTime)); }}private Instance findLeakingReference(String key, Snapshot snapshot) { // 获得KeyedWeakReference类的引用 ClassObj refClass = snapshot.findClass(KeyedWeakReference.class.getName()); ListkeysFound = new ArrayList<>(); for (Instance instance : refClass.getInstancesList()) { List values = classInstanceValues(instance); String keyCandidate = asString(fieldValue(values, "key")); // 如果某类的key和我们传入的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);}
最终根据泄漏的类来进行引用跟踪,并且返回AnalysisResult
分析结果。
com.squareup.haha
来进行hropf文件分析以及引用树的建立,其内存泄漏的分析应该和MAT等工具原理一致。 构造好了AnalysisResult
分析结果后,通过AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
方法将结果发送到listenerClassName
,这个就是我们上面说的DisplayLeakService
,顾名思义,这个就是用于显示泄漏的Service:
这里最主要的内存泄漏分析过程并没有写出来,主要是其使用了另一个库进行分析,实在是没有精力去分析了。
简单总结下:通过注册
application.registerActivityLifecycleCallbacks
注册对Activity生命周期回调的监听,在onDestory后进行内存泄漏分析
通过
KeyedWeakReference
和ReferenceQueue
对弱引用进行是否回收判断,如果回收则将该弱引用放入ReferenceQueue
中。
转载地址:http://xcvwo.baihongyu.com/