作者:潇湘夜雨123 https://www.jianshu.com/p/8c06c9c9317c 本文由作者投稿并原创发布
LeakCanary简单介绍
LeakCanary是Square公司基于MAT开源的一个内存泄漏检测工具,在发生内存泄漏的时候LeakCanary会自动显示泄漏信息。
LeakCanary简单使用
配置build.gradle:
代码语言:javascript复制debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4'
releaseImplementation'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4'
自定义Application
代码语言:javascript复制public class ExampleApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
setupLeakCanary();
}
protected void setupLeakCanary() {
if (LeakCanary.isInAnalyzerProcess(this)) {
return;
}
LeakCanary.install(this);
}
如果当前的进程是用来给LeakCanary 进行堆分析的则return,否则会执行LeakCanary的install方法。这样我们就可以使用LeakCanary了,如果检测到某个Activity 有内存泄露,LeakCanary 就会给出提示。
LeakCanary原理
LeakCanary原理是将检测的对象放入弱引用中并且关联到引用队列中,查看引用队列中是否存在引用,如果发现泄露,dump出信息进行分析。 先从LeakCanary的install入手。
代码语言:javascript复制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);
}
install方法用于创建AndroidRefWatcherBuilder类,最终调用buildAndInstall方法。
代码语言:javascript复制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;
}
/** Creates a {@link RefWatcher}. */
public final RefWatcher build() {
if (isDisabled()) {
return RefWatcher.DISABLED;
}
buildAndInstall方法用于监听activity和fragment内存泄露,通过build方法创建RefWatcher从而创建ActivityRefWatcher和FragmentRefWatcher。
代码语言:javascript复制 public static void install(Context context, RefWatcher refWatcher) {
Application application = (Application) context.getApplicationContext();
ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
}
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override
public void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
};
首先进入监听activity类的ActivityRefWatcher类install中。 给Application注册ActivityLifecycleCallbacks,让activity中的onDestroy方法关联到refWatcher.watch中。 Application通过ActivityLifecycleCallbacks使用接口提供了一套回调方法, 对Activity的生命周期事件检测。ActivityLifecycleCallbacks使用要求API 14 。
代码语言:javascript复制 private static final String SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME =
"com.squareup.leakcanary.internal.SupportFragmentRefWatcher";
public static void install(Context context, RefWatcher refWatcher) {
List<FragmentRefWatcher> fragmentRefWatchers = new ArrayList<>();
if (SDK_INT >= O) {
fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher));
}
try {
Class<?> fragmentRefWatcherClass = Class.forName(SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME);
Constructor<?> constructor =
fragmentRefWatcherClass.getDeclaredConstructor(RefWatcher.class);
FragmentRefWatcher supportFragmentRefWatcher =
(FragmentRefWatcher) constructor.newInstance(refWatcher);
fragmentRefWatchers.add(supportFragmentRefWatcher);
} catch (Exception ignored) {
}
if (fragmentRefWatchers.size() == 0) {
return;
}
Helper helper = new Helper(fragmentRefWatchers);
Application application = (Application) context.getApplicationContext();
application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks);
}
private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
for (FragmentRefWatcher watcher : fragmentRefWatchers) {
watcher.watchFragments(activity);
}
}
};
再进入监听fragment的FragmentRefWatcher中,其内部类Helper将实现FragmentRefWatcher接口的SupportFragmentRefWatcher和AndroidOFragmentRefWatcher加入到fragmentRefWatchers数组中。 因为支持v4包下的fragment在另一个moudle中,所以通过反射的方式创建对象。 最后注册ActivityLifecycleCallbacks监听Activity的onCreated方法。
代码语言:javascript复制 private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
new FragmentManager.FragmentLifecycleCallbacks() {
@Override
public void onFragmentDestroyed(FragmentManager fm, Fragment fragment) {
refWatcher.watch(fragment);
}
};
@Override public void watchFragments(Activity activity) {
FragmentManager fragmentManager = activity.getFragmentManager();
fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
}
因为SupportFragmentRefWatcher和AndroidOFragmentRefWatcher是FragmentRefWatcher的实现类。最终会回调实现类的watchFragments方法,最终fragment会注册FragmentLifecycleCallbacks方法监听fragment。 activity和fragment最终都会调用RefWatcher中的watch方法,所以要先进入RefWatcher类中 看具体实现。
代码语言:javascript复制public final class RefWatcher {
public static final RefWatcher DISABLED = new RefWatcherBuilder<>().build();
private final WatchExecutor watchExecutor;
private final DebuggerControl debuggerControl;
private final GcTrigger gcTrigger;
private final HeapDumper heapDumper;
private final Set<String> retainedKeys;
private final ReferenceQueue<Object> queue;
private final HeapDump.Listener heapdumpListener;
private final ExcludedRefs excludedRefs;
private final boolean computeRetainedHeapSize;
}
WatchExecutor watchExecutor:查找检测内存泄露的对象 DebuggerControl debuggerControl:检测当前是否正在调试中 GcTrigger gcTrigger:调用gc方法 HeapDumper heapDumper:dump内存泄露产生的文件 SetretainedKeys:存储引用key(待检测或已经产生泄露) ReferenceQueue queue:引用队列,存储待检测的弱引用 HeapDump.Listener heapdumpListener:HeapDumper的回调 ExcludedRefs excludedRefs:排除系统引起的内存泄露 boolean computeRetainedHeapSize:检测泄露时是否计算堆的大小,默认为false
查看RefWatcher中的watch方法。
代码语言:javascript复制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);
}
首先对retainedKeys添加一个唯一的key,并且将待检测的watchedReference加入转化为弱引用加入到引用队列中,开启异步线程ensureGoneAsync分析是否泄露。
代码语言:javascript复制 private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
在ensureGoneAsync方法中,这里通过WatchExecutor接口的实现类AndroidWatchExecutor的,最后handler加入到消息队列中。
代码语言:javascript复制 @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);//gc时间到调用watch方法的时间差
removeWeaklyReachableReferences();//清除到达引用队列中的弱引用
if (debuggerControl.isDebuggerAttached()) {//判断是否为调试阶段
// The debugger can create false leaks.
return RETRY;
}
if (gone(reference)) {//判断检测对象是否达到引用队列
return DONE;
}
gcTrigger.runGc();//调用gc回收
removeWeaklyReachableReferences();//再次清除到达引用队列中的弱引用
//再次判断检测对象是否达到引用队列如果未到达,产生heap文件。
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.Durations durations =
new HeapDump.Durations(watchDurationMs, gcDurationMs, heapDumpDurationMs);
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs,
computeRetainedHeapSize, durations));
}
return DONE;
}
在ensureGone方法中,会清除引用队列中的弱引用,接着检测对象是否达到引用队列,然后调用gcTrigger.runGc进行gc回收,然后会再次清除引用队列中的弱引用。最终会调用heapdumpListener.analyze方法进行回调。
代码语言:javascript复制public final class ServiceHeapDumpListener implements HeapDump.Listener
@Override public void analyze(HeapDump heapDump) {
checkNotNull(heapDump, "heapDump");
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
heapdumpListener是HeapDump的内部接口。在ServiceHeapDumpListener进行实现然后会调用 HeapAnalyzerService.runAnalysis方法。
代码语言:javascript复制public final class HeapAnalyzerService extends ForegroundService
implements AnalyzerProgressListener {
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);
context.startService(intent);
}
@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);
AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
heapDump.computeRetainedHeapSize);
AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
}}
进入HeapAnalyzerService.runAnalysis方法可以看出会启动HeapAnalyzerService,HeapAnalyzerService是ForegroundService的子类。最终会调用onHandleIntentInForeground方法。 onHandleIntentInForeground方法会首先判断intent是否为空,然后调用heapAnalyzer.checkForLeak进行文件分析。最后调用AbstractAnalysisResultService.sendResultToListener进行回调显示结果。 进入HeapAnalyzer这个类中的checkForLeak方法 。
代码语言:javascript复制 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);
//将heap文件封装成MemoryMappedFileBuffer
HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
//创建hprof解析器,解析hprof文件
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);
//检测解析的结果是否存在referenceKey的引用
Instance leakingRef = findLeakingReference(referenceKey, snapshot);
//根据leakingRef的结果调用noLeak或者寻找路径
// 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));
}
}
首先将heap文件封装成HprofParser,然后将hprof解析成内存快照,并且去掉重复的引用。最后根据内存快照找出泄露对象的最短路径。