基本使用
-
1.5.3版本使用简介
- 添加依赖
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4'
- 自定义Application
class App:Application() {
override fun onCreate() {
super.onCreate()
LeakCanary.install(this)
}
}
-
2.0版本呢使用简介
- 添加依赖
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7.1'
- 自定义Application
class App:Application() {
override fun onCreate() {
super.onCreate()
var config = AppWatcher.config.copy(watchFragmentViews = false)
AppWatcher.config = config
}
}
1.5.4版本原理分析
1、LeakCanary.install
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);
}
public RefWatcher buildAndInstall() {
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
LeakCanary.enableDisplayLeakActivity(context);
ActivityRefWatcher.install((Application) context, refWatcher);
}
return refWatcher;
}
- install方法执行初始化工作,它在Application的onCreate方法中调用。
- refWatcher方法
创建并返回了AndroidRefWatcherBuilder类对象。AndroidRefWatcherBuilder的核心工作是初始化,并构建RefWatcher类。RefWatcher是LeakCanary的一个核心类,核心工作是检查时候发生内存泄漏。
- listenerServiceClass方法
分析结果监听。通过ServiceHeapDumpListener包装了listenerServiceClass类
入参:DisplayLeakService类。它继承自AbstractAnalysisResultService。主要工作是将将内存泄漏的信息,以前台通知形式告诉开发者。
- excludeRefs方法
排除系统bug
- buildAndInstall方法
buildAndInstall方法做了两件事情,一、创建了RefWatcher对象,二、关联RefWatcher和Activity的生命周期。具体如下1.1和1.2代码。
1.1、AndroidRefWatcherBuilder.buildAndInstall
public RefWatcher buildAndInstall() {
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
LeakCanary.enableDisplayLeakActivity(context);
ActivityRefWatcher.install((Application) context, refWatcher);
}
return refWatcher;
}
- 通过build方法创建RefWatcher对象
- 正常来说,RefWatcher不是DISABLE,所以会走到if条件里面来。
- ActivityRefWatcher.install关联RefWatcher和Activity的生命周期
接下来,我们进到ActivityWatcher.install方法,看看到底是怎么关联两者的。
1.2、ActivityRefWatcher.install
public static void install(Application application, RefWatcher refWatcher) {
new ActivityRefWatcher(application, refWatcher).watchActivities();
}
public void watchActivities() {
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new Application.ActivityLifecycleCallbacks() {
......
@Override public void onActivityDestroyed(Activity activity) {
ActivityRefWatcher.this.onActivityDestroyed(activity);
}
};
- 静态方法install创建了ActivityRefWatcher对象,该对象实现了生命周期回调lifecycleCallbacks。
- 调用ActivityRefWatcher的watchActivities方法,将lifecycleCallbacks和RefWatcher关联。
- 具体为lifecycleCallbacks.onActivityDestroyed回调方法会调用onActivityDestroyed方法,这个方法最终调用了RefWatcher.watch(activity)。
1.3、总结:
至此,LeakCanary的初始化工作已经全部结束。install方法的两个重要工作是创建RefWatcher,关联RefWacher和生命周期。
2、Refwatcher.watch
当Activity执行finish时,回调onDestory同时会触发ActivityRefWatcher的生命周期回调lifecycleCallbacks。根据1.2中的分析,此时会调用RefWatcher.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);
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
ensureGoneAsync(watchStartNanoTime, reference);
}
- UUID.randomUUID.toStirng()生成随机的字符串并赋值给key,这个key代表了一个Activity对象。
- 通过上面的key,引用队列queue,watchedReference(待内存泄漏分析的activity)传入KeyWeakReference得到一个自定义的弱引用对象。
- watch传入了activity对象。通过activity,引用队列queue, key,得到了一个KeyedWeakReference 类型的弱引用对象reference。
- 调用ensureGone检查activity是否发生内存泄漏。
3、Refwatcher.ensureGoneAsync
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
- ensureGoneAsync异步调用ensureGone,执行内存泄漏分析
4、Refwatcher.ensureGone
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
removeWeaklyReachableReferences();
if (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks.
return RETRY;
}
if (gone(reference)) {
return DONE;
}
gcTrigger.runGc();
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);
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
}
return DONE;
}
- removeWeaklyReachableReferences方法遍历引用队列,得到每一个弱引用对象,调用retainedKey.remove方法,清除列中中的key对象。前面我们讲到,retainedKey中每一个元素间接表示了一个需要被检查泄漏的对象。遍历和清除之后,剩下的元素,有可能存在内存泄漏。removeWeaklyReachableReferences的相关代码如下:
- gone方法检查当前对象是否被GC。如果对象被GC,他的弱引用对象将会被放到引用队列。那么在removeWeaklyReachebleReference方法对将其弱引用的key从retainedKeys中清除。
- 如果对象没有被GC,调用gcTrigger.runGc()方法,再执行一次GC,防止误报。GC之后再次执行removeWeaklyReachableReferences,和gone方法,检查对象时候回收。
- 如果对象没有被回收,我们认定对象已经出现内存泄漏的情况。
4.1 、RefWatcher.removeWeaklyReachableReferences
private void removeWeaklyReachableReferences() {
// WeakReferences are enqueued as soon as the object to which they point to becomes weakly
// reachable. This is before finalization or garbage collection has actually happened.
KeyedWeakReference ref;
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key);
}
}
- 如果对象被垃圾回收器回收,其弱引用就会被加入到引用队列queue中。此时通过遍历引用队列,将KetWeakReference的key从集合中删除。
4.2、RefWatcher.gone
private boolean gone(KeyedWeakReference reference) {
return !retainedKeys.contains(reference.key);
}
- 用来判断是否存在内存泄漏的可能。
- 如果没有内存泄漏,4.1的removeWeaklyReacheableReference方法就会将key值从集合中删除。换言之:如果当前集合包含了key,就可能存在内存泄漏。
4.3、HeapDumper.dumpHeap
@Override
public File dumpHeap() {
File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();
if (heapDumpFile == RETRY_LATER) {
return RETRY_LATER;
}
FutureResult<Toast> waitingForToast = new FutureResult<>();
showToast(waitingForToast);
if (!waitingForToast.wait(5, SECONDS)) {
CanaryLog.d("Did not dump heap, too much time waiting for Toast.");
return RETRY_LATER;
}
Toast toast = waitingForToast.get();
try {
Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
cancelToast(toast);
return heapDumpFile;
} catch (Exception e) {
CanaryLog.d(e, "Could not dump heap");
return RETRY_LATER;
}
}
- HeapDumper只是一个接口,他的实现在AndroidHeapDumper。
- 19行,调用Debug.dumpHprofData方法,获取hprof文件。
4.4、ServiceHeapDumpListener.analyze
@Override public void analyze(HeapDump heapDump) {
checkNotNull(heapDump, "heapDump");
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
- analyze方法传入的是HeapDump对象。HeapAnalyzerService是IntentService类型,runAnalysis实现了异步调用,会走到HeapAnalyzerService.onHanderIntent方法中去。
4.5、HeapAnalyzerService.onHandlerIntent
@Override
protected void onHandleIntent(Intent intent) {
if (intent == null) {
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);
}
- 在子线程中执行内存泄漏分析
- checkForLeak检查内存泄漏并返回AnalysisResult分析结果
4.5.1、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 {
HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
HprofParser parser = new HprofParser(buffer);
Snapshot snapshot = parser.parse();
deduplicateGcRoots(snapshot);
Instance leakingRef = findLeakingReference(referenceKey, snapshot);
if (leakingRef == null) {
return noLeak(since(analysisStartNanoTime));
}
return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef);
} catch (Throwable e) {
return failure(e, since(analysisStartNanoTime));
}
}
- checkForLeak方法用来检查内存泄漏并返回检查结果AnalysisResult。第10行到12行是解析hprof文件,得到Snapshot。通过snapshot我们可以进一步分析内存泄漏。
- findLeakingReference根据refrenceKey查找snapshot中的发生内存泄漏的KeyedWeakReference对象(前面提到的自定义弱引用对象)。
- 如果leakingRef为空,说明没有内存泄漏
- leakRef不为空,说明发生了内存泄漏,此时需要对内存泄漏进行分析。通过findLeakTrace调用查找引用链。
- 4.5.1.1、HeapAnalyzerService.findLeakingReference
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"));
if (keyCandidate.equals(key)) {
return fieldValue(values, "referent");
}
keysFound.add(keyCandidate);
}
throw new IllegalStateException(
"Could not find weak reference with key " + key + " in " + keysFound);
}
- 通过SnapShot.findClass查找所有的KeyedWeakReference对象。遍历instance,找到Key为key的KeyedWeakReference。并返回,如果没有找到就抛出异常
- 4.5.2.2、HeapAnalyzer.findLeakTrace
private AnalysisResult findLeakTrace(long analysisStartNanoTime, Snapshot snapshot,
Instance leakingRef) {
ShortestPathFinder pathFinder = new ShortestPathFinder(excludedRefs);
ShortestPathFinder.Result result = pathFinder.findPath(snapshot, leakingRef);
if (result.leakingNode == null) {
return noLeak(since(analysisStartNanoTime));
}
LeakTrace leakTrace = buildLeakTrace(result.leakingNode);
String className = leakingRef.getClassObj().getClassName();
snapshot.computeDominators();
Instance leakingInstance = result.leakingNode.instance;
long retainedSize = leakingInstance.getTotalRetainedSize();
if (SDK_INT <= N_MR1) {
retainedSize += computeIgnoredBitmapRetainedSize(snapshot, leakingInstance);
}
return leakDetected(result.excludingKnownLeaks, className, leakTrace, retainedSize,
since(analysisStartNanoTime));
}
- 得到内存泄漏的instance之后,checkForLeak方法调用了findLeakTrace进行内存分析,并返回AnalysisResult。
- buildLeakTrace方法得到内存泄漏的引用链
- leakingInstance.getTotalRetainedSize()得到引用链长度
- leakDetected返回AnalysisResult
4.5.2、AbstractAnalysisResultService.sendResultToListener
public static void sendResultToListener(Context context, String listenerServiceClassName,
HeapDump heapDump, AnalysisResult result) {
Class<?> listenerServiceClass;
try {
listenerServiceClass = Class.forName(listenerServiceClassName);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
Intent intent = new Intent(context, listenerServiceClass);
intent.putExtra(HEAP_DUMP_EXTRA, heapDump);
intent.putExtra(RESULT_EXTRA, result);
context.startService(intent);
}
public AbstractAnalysisResultService() {
super(AbstractAnalysisResultService.class.getName());
}
@Override protected final void onHandleIntent(Intent intent) {
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAP_DUMP_EXTRA);
AnalysisResult result = (AnalysisResult) intent.getSerializableExtra(RESULT_EXTRA);
try {
onHeapAnalyzed(heapDump, result);
} finally {
//noinspection ResultOfMethodCallIgnored
heapDump.heapDumpFile.delete();
}
}
- AbstractAnalysisResultService是IntentService,最终会回调onHandleIntent。
- onHandleIntent调用onHeapAnalyzed方法,这个方法是在DisplayLeakService类中实现的。
4.5.3、DisplayLeakService.onHeapAnalyzed
@Override protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) {
String leakInfo = leakInfo(this, heapDump, result, true);
CanaryLog.d("%s", leakInfo);
boolean resultSaved = false;
boolean shouldSaveResult = result.leakFound || result.failure != null;
if (shouldSaveResult) {
heapDump = renameHeapdump(heapDump);
resultSaved = saveResult(heapDump, result);
}
PendingIntent pendingIntent;
String contentTitle;
String contentText;
if (!shouldSaveResult) {
contentTitle = getString(R.string.leak_canary_no_leak_title);
contentText = getString(R.string.leak_canary_no_leak_text);
pendingIntent = null;
} else if (resultSaved) {
pendingIntent = DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey);
if (result.failure == null) {
String size = formatShortFileSize(this, result.retainedHeapSize);
String className = classSimpleName(result.className);
if (result.excludedLeak) {
contentTitle = getString(R.string.leak_canary_leak_excluded, className, size);
} else {
contentTitle = getString(R.string.leak_canary_class_has_leaked, className, size);
}
} else {
contentTitle = getString(R.string.leak_canary_analysis_failed);
}
contentText = getString(R.string.leak_canary_notification_message);
} else {
contentTitle = getString(R.string.leak_canary_could_not_save_title);
contentText = getString(R.string.leak_canary_could_not_save_text);
pendingIntent = null;
}
// New notification id every second.
int notificationId = (int) (SystemClock.uptimeMillis() / 1000);
showNotification(this, contentTitle, contentText, pendingIntent, notificationId);
afterDefaultHandling(heapDump, result, leakInfo);
}
- onHeapAnalyzed将内存泄漏的信息以前台通知发送
至此,LeakCanary的内存分析检测流程全部分析完