LeakCanary使用及浅析
基本使用步骤:
添加依赖:
implementation 'com.squareup.leakcanary:leakcanary-android:1.5'
import com.squareup.leakcanary.LeakCanary;
import com.squareup.leakcanary.RefWatcher;
public class MyApplication extends Application {
RefWatcher refWatcher;
@Override
public void onCreate() {
super.onCreate();
refWatcher = LeakCanary.install(this);
refWatcher.watch(this);
}
}
从入口开始分析:
1 public static RefWatcher install(Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
1.1
listenerServiceClass(DisplayLeakService.class)
绑定DisplayLeakService 服务类,这个类负责将内存泄漏的消息通知给你
public class DisplayLeakService extends AbstractAnalysisResultService {
@Override protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) {
pendingIntent = DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey);
}
DisplayLeakActivity.java
public static PendingIntent createPendingIntent(Context context, String referenceKey) {
Intent intent = new Intent(context, DisplayLeakActivity.class);
intent.putExtra(SHOW_LEAK_EXTRA, referenceKey);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
return PendingIntent.getActivity(context, 1, intent, FLAG_UPDATE_CURRENT);
}
1.2
excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
排除开发中可以忽略的泄漏路径(一般是系统级别BUG),这些枚举在AndroidExcludedRefs这个类当中定义
1.3
buildAndInstall();
public RefWatcher buildAndInstall() {
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
LeakCanary.enableDisplayLeakActivity(context);
ActivityRefWatcher.installOnIcsPlus((Application) context, refWatcher);
}
return refWatcher;
}
实例化refWatcher 这个对象用来判断泄露对象
ActivityRefWatcher.java
public static void installOnIcsPlus(Application application, RefWatcher refWatcher) {
if (SDK_INT < ICE_CREAM_SANDWICH) {
// If you need to support Android < ICS, override onDestroy() in your base activity.
return;
}
ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
activityRefWatcher.watchActivities();
}
public void watchActivities() {
// Make sure you don't get installed twice.
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new Application.ActivityLifecycleCallbacks() {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
...
@Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override public void onActivityDestroyed(Activity activity) {
ActivityRefWatcher.this.onActivityDestroyed(activity);
}
};
这个地方是关键,通过application注册监听,统一管理activity的生命周期,LeakCanay是在onDestory()方法实现对Activity监控.
2 refWatcher.watch(this);
RefWatcher.java
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);
}
final class KeyedWeakReference extends WeakReference
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)) { //说明相应的Activity没有泄露
return DONE;
}
gcTrigger.runGc(); //手动GC
removeWeaklyReachableReferences(); //再次判断是否回收完全
if (!gone(reference)) { //如果还有对象没有被回收,说明存在内存泄露的可能,dump内存快照.hprof进行进一步分析
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);
//内存快照分析,在开始绑定的服务ServiceHeapDumpListener中处理
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
}
return DONE;
}
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);
}
}
ServiceHeapDumpListener.java
@Override public void analyze(HeapDump heapDump) {
checkNotNull(heapDump, "heapDump");
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
public final class HeapAnalyzerService extends IntentService {
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);
}
@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);
}
}
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();//Snotshot包含对象引用的所有路径
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) {
ClassObj refClass = snapshot.findClass(KeyedWeakReference.class.getName());
List keysFound = new ArrayList<>();
for (Instance instance : refClass.getInstancesList()) {
List 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快照中找到第一个弱引用,然后根据遍历这个对象的所有实例,
如果key值和最开始定义封装的key值相同,那么返回这个泄漏对象,就是已近在快照中定位到了泄漏对象了。
private AnalysisResult findLeakTrace(long analysisStartNanoTime, Snapshot snapshot,
Instance leakingRef) {
ShortestPathFinder pathFinder = new ShortestPathFinder(excludedRefs);
ShortestPathFinder.Result result = pathFinder.findPath(snapshot, leakingRef);
...
long retainedSize = leakingInstance.getTotalRetainedSize();
//找出内存泄露的最短路径
retainedSize += computeIgnoredBitmapRetainedSize(snapshot, leakingInstance);
return leakDetected(result.excludingKnownLeaks, className, leakTrace, retainedSize,
since(analysisStartNanoTime));
}
总结: LeakCanay的入口是在application的onCreate()方法中声明的,其实用的就是Application的ActivityLifecycleCallbacks回调接口监听所有activity的onDestory()的,在这个方法进行RefWatcher.watch对这个对象进行监控。处理过程:封装成带key的弱引用对象,然后GC看弱引用对象有没有回收,没有回收的话就怀疑是泄漏了,需要二次确认。然后生成HPROF文件,分析这个快照文件有没有存在带这个key值的泄漏对象,如果没有,那么没有泄漏,否则找出最短路径。