1、LeakCanary的使用
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
if (LeakCanary.isInAnalyzerProcess(this)) {
return;
}
LeakCanary.install(this);
}
}
LeakCanary.install(this)是分析的入口
2、LeakCanary.install(Application application)
public static RefWatcher install(Application application) {
//refWatcher返回AndroidRefWatcherBuilder对象
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build()) //excludedRefs 排除不希望检测的对象
.buildAndInstall(); //见3
}
public static AndroidRefWatcherBuilder refWatcher(Context context) {
return new AndroidRefWatcherBuilder(context);
}
创建了AndroidRefWatcherBuilder对象,接着调用了buildAndInstall方法
3、AndroidRefWatcherBuilder.buildAndInstall()
public RefWatcher buildAndInstall() {
RefWatcher refWatcher = build();// 见4 调了父类RefWatcherBuilder的build方法
if (refWatcher != DISABLED) {
LeakCanary.enableDisplayLeakActivity(context);
ActivityRefWatcher.install((Application) context, refWatcher);//见5
}
return refWatcher;
}
创建出RefWatcher对象,接着ActivityRefWatcher去installl
4、RefWatcherBuilder.build()
public final RefWatcher build() {
if (isDisabled()) {
return RefWatcher.DISABLED;
}
//下面是构建RefWatcher对象所需参数
ExcludedRefs excludedRefs = this.excludedRefs;
if (excludedRefs == null) {
excludedRefs = defaultExcludedRefs();
}
HeapDump.Listener heapDumpListener = this.heapDumpListener;
if (heapDumpListener == null) {
heapDumpListener = defaultHeapDumpListener();
}
DebuggerControl debuggerControl = this.debuggerControl;
if (debuggerControl == null) {
debuggerControl = defaultDebuggerControl();
}
HeapDumper heapDumper = this.heapDumper;
if (heapDumper == null) {
heapDumper = defaultHeapDumper();
}
WatchExecutor watchExecutor = this.watchExecutor;
if (watchExecutor == null) {
watchExecutor = defaultWatchExecutor();
}
GcTrigger gcTrigger = this.gcTrigger;
if (gcTrigger == null) {
gcTrigger = defaultGcTrigger();
}
//返回新建的RefWatcher实例
return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,
excludedRefs);
}
构建了RefWatcher对象。顾名思义,RefWatcher对象是用来观察引用的
5、ActivityRefWatcher.install()
public static void install(Application application, RefWatcher refWatcher) {
//创建ActivityRefWatcher用于检测Activity
new ActivityRefWatcher(application, refWatcher).watchActivities();
}
public void watchActivities() {
// Make sure you don't get installed twice.
stopWatchingActivities();
//注册activity生命周期回调
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new Application.ActivityLifecycleCallbacks() {
...
@Override public void onActivityDestroyed(Activity activity) {
//在Activity.onDestoryed回调时,运行ActivityRefWatcher.onActivityDestroyed(activity)方法
ActivityRefWatcher.this.onActivityDestroyed(activity);//见6
}
};
在Activity.onDestoryed回调时,运行ActivityRefWatcher.onActivityDestroyed(activity)方法
6、ActivityRefWatcher.onActivityDestroyed(activity)
void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);//见7
}
调用了refWatcher.watch()方法,refWatcher就是第5小结构建ActivityRefWatcher对象构建方法传入的
7、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(); //生产和被观察关联的key
retainedKeys.add(key);//retainedKeys保存key
//创建KeyedWeakReference,它是WeakReference的子类,增加了key和name属性。
//把检测对象和ReferenQueue对象也传了进去
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
ensureGoneAsync(watchStartNanoTime, reference);//见8
}
这里注意下,RefWatcher.watch()接收到的是Object对象,也就是说LeakCanary本质上是支持所有对象泄漏检测的,在检测的地方运行RefWatcher.watch()方法即可。
8、RefWatcher.ensureGoneAsync()
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);//见9
}
});
}
watchExecutor是一个封装了Handler、具有重试功能的执行器。传递给WatchExecutor.execute方法的Retryable对象,在经过5秒延迟后会执行run方法。(5秒延迟是跟ANR有关???),run方法调了ensureGone方法
9、RefWatcher.ensureGone()
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
removeWeaklyReachableReferences();//见10 清除入队的弱引用
if (debuggerControl.isDebuggerAttached()) {
// debug模式下重试.
return RETRY;
}
//gone方法如果检测到对象被回收,说明没有泄露
if (gone(reference)) {//见10
return DONE;
}
//触发GC
gcTrigger.runGc();
removeWeaklyReachableReferences();//清除入队的弱引用
if (!gone(reference)) {//再次判断是否检测对象被回收
//仍然没有正常回收检测对象
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
//dump出内存信息
File heapDumpFile = heapDumper.dumpHeap();
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
//构建HeapDump去分析内存泄露的详细信息
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));//见11
}
return DONE;
}
首先运行removeWeaklyReachableReferences清除入队的弱引用,然后gone方法判断检测对象是否被回收。若未被回收触发一次gc,接着再清除入队的弱引用。如果仍未被回收就dump出hprof文件去分析。
看下removeWeaklyReachableReferences和gone方法的实现
10、RefWatcher.gone() & removeWeaklyReachableReferences()
private void removeWeaklyReachableReferences() {
KeyedWeakReference ref;
//移除入队的Reference
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key); //retainedKeys删除关联的key
}
}
private boolean gone(KeyedWeakReference reference) {
return !retainedKeys.contains(reference.key);
}
从queue逐个移除KeyedWeakReference对象,接着移除retainedKeys中和KeyedWeakReference对象关联的key,也就是如果检测对象已经到达过引用队列,被正常回收,retainedKeys是不再包含关联的key。所以gone方法可以通过判断retainedKeys是否包含key来确认被检测对像是否被回收。
11、ServiceHeapDumpListener.analyze()
heapdumpListener.analyze会走到HeapAnalyzerService.onHandleIntent()方法
protected void onHandleIntent(Intent intent) {
String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
//创建分析类
HeapAnalyzer heapAnalyzer = new HeapAnalyzer(heapDump.excludedRefs);
//见12 分析流程
AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey);
//分析后
AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
}
重点的一步调了heapAnalyzer.checkForLeak分析
12、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 {
//MemoryMappedFileBuffer封装heapDumpFile
HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
//HprofParser分析hprof文件
HprofParser parser = new HprofParser(buffer);
//分析结果封装在Snapshot对象中
Snapshot snapshot = parser.parse();//见13
//去重GcRoot
deduplicateGcRoots(snapshot);
//通过key找到检测对象
Instance leakingRef = findLeakingReference(referenceKey, snapshot);//见14
// 没有泄露
if (leakingRef == null) {
return noLeak(since(analysisStartNanoTime));
}
//找到最短泄露路径
return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef); //见15
} catch (Throwable e) {
return failure(e, since(analysisStartNanoTime));
}
}
主要有几个步骤:
- 把heapDumpFile封装成MemoryMappedFileBuffer。
- 通过HprofParser对象去分析MemoryMappedFileBuffer。
- 去除Snapshot中重复的GcRoot。
- 通过key找到Snapshot中保存的泄露对象。
- 分析GcRoot到泄露对象的最短路径。
checkForLeak方法有两个东西的没搞懂,一个Snapshot的数据结构,另一个是findLeakingReference方法。先看看Snapshot的数据结构。
下面信息根据debug追踪、注释、类属性和命名等方式得出的结果,未必正确,希望有大牛指点一二
13、Snapshot 数据结构
/*
* A snapshot of all of the heaps, and related meta-data, for the runtime at a given instant.
*
* There are three possible heaps: default, app and zygote. GC roots are always reported in the
* default heap, and they are simply references to objects living in the zygote or the app heap.
* During parsing of the HPROF file HEAP_DUMP_INFO chunks change which heap is being referenced.
*/
public class Snapshot {
private static final String JAVA_LANG_CLASS = "java.lang.Class";
// Special root object used in dominator computation for objects reachable via multiple roots.
public static final Instance SENTINEL_ROOT = new RootObj(RootType.UNKNOWN);
private static final int DEFAULT_HEAP_ID = 0;
final HprofBuffer mBuffer;
//堆信息
ArrayList<Heap> mHeaps = new ArrayList<Heap>();
Heap mCurrentHeap;
private ImmutableList<Instance> mTopSort;
private Dominators mDominators;
// 保存各种Reference以及它的子类的Class信息
private THashSet<ClassObj> mReferenceClasses = new THashSet<ClassObj>();
private int[] mTypeSizes;
private long mIdSizeMask = 0x00000000ffffffffl;
....
//省略很多代码
}
从源码上来看Snapshot主要有:
- mHeaps,Heap的列表,有四种可能default、app、image和zygote。GcRoot总是保存在default heap,并且GcRoot仅仅是指向保存在app、zygote的对象。
- mReferenceClasses ,保存各种Reference以及它的子类的Class信息,之前使用的KeyedWeakReference也保存在这里
接着看看Heap的数据结构
public class Heap {
private final int mId;
private final String mName;
// 每个item对应着内存中栈的一帧
TLongObjectHashMap<StackFrame> mFrames = new TLongObjectHashMap<StackFrame>();
// StackTrace对应着内存中的栈,每个StackTrace里面包含多个StackFrame
TIntObjectHashMap<StackTrace> mTraces = new TIntObjectHashMap<StackTrace>();
// 根对象,如内部字符串、jni局部变量等
ArrayList<RootObj> mRoots = new ArrayList<RootObj>();
// 线程列表
TIntObjectHashMap<ThreadObj> mThreads = new TIntObjectHashMap<ThreadObj>();
// Class信息
TLongObjectHashMap<ClassObj> mClassesById = new TLongObjectHashMap<ClassObj>();
Multimap<String, ClassObj> mClassesByName = ArrayListMultimap.create();
// 上面 Class对应的实例列表
private final TLongObjectHashMap<Instance> mInstances = new TLongObjectHashMap<Instance>();
// 关联的snapshot对象
Snapshot mSnapshot;
...
}
Heap保存了
- StackFrame,对应着内存中栈的一帧。
- StackTrace,对应着内存中的栈。
- RootObj, 根对象
- ThreadObj ,线程
- ClassObj ,Class信息
- Instance,ClassObj的实例
14、HeapAnalyzer.findLeakTrace()
private AnalysisResult findLeakTrace(long analysisStartNanoTime, Snapshot snapshot,
Instance leakingRef) {
//创建最短路径Finder
ShortestPathFinder pathFinder = new ShortestPathFinder(excludedRefs);
//查找找GcRoot到泄露对象的最短路径
ShortestPathFinder.Result result = pathFinder.findPath(snapshot, leakingRef);//见15
...
//封装分析的结果,省略了
}
这一步主要是创建最短路径Finder,然后执行findPath方法去寻找
15、ShortestPathFinder.findPath()
Result findPath(Snapshot snapshot, Instance leakingRef) {
clearState();
canIgnoreStrings = !isString(leakingRef);
把GcRoot加入到队列中
enqueueGcRoots(snapshot);
boolean excludingKnownLeaks = false;
LeakNode leakingNode = null;
//遍历队列,GcRoot就是加入了toVisitQueue或toVisitIfNoPathQueue其中一个
while (!toVisitQueue.isEmpty() || !toVisitIfNoPathQueue.isEmpty()) {
LeakNode node;
if (!toVisitQueue.isEmpty()) {
node = toVisitQueue.poll();
} else {
node = toVisitIfNoPathQueue.poll();
if (node.exclusion == null) {
throw new IllegalStateException("Expected node to have an exclusion " + node);
}
excludingKnownLeaks = true;
}
// 找到了泄露对象,结束搜索
if (node.instance == leakingRef) {
leakingNode = node;
break;
}
if (checkSeen(node)) {
continue;
}
//根据node类型分case
if (node.instance instanceof RootObj) {
visitRootObj(node); //见16 -------------【吸引注意力】------------!!!!!------------------------------
} else if (node.instance instanceof ClassObj) {
visitClassObj(node);
} else if (node.instance instanceof ClassInstance) {
visitClassInstance(node);
} else if (node.instance instanceof ArrayInstance) {
visitArrayInstance(node);
} else {
throw new IllegalStateException("Unexpected type for " + node.instance);
}
}
return new Result(leakingNode, excludingKnownLeaks);
}
这一步首先把GcRoot加入队列,然后遍历队列。仅仅这个方法看不出什么,接着看看被调用的visitRootObj方法到底做了什么
16、ShortestPathFinder.visitRootObj()
private void visitRootObj(LeakNode node) {
RootObj rootObj = (RootObj) node.instance;
获取Instance
Instance child = rootObj.getReferredInstance();
if (rootObj.getRootType() == RootType.JAVA_LOCAL) {
Instance holder = HahaSpy.allocatingThread(rootObj);
// We switch the parent node with the thread instance that holds
// the local reference.
Exclusion exclusion = null;
if (node.exclusion != null) {
exclusion = node.exclusion;
}
LeakNode parent = new LeakNode(null, holder, null, null, null);
LeakTraceElement.Type referenceType)
//入队,enqueue方法内部构建parent和child关系,就是通过LeakNode对象的parent属性关联
//LeakNode childNode = new LeakNode(exclusion, child, parent, referenceName, referenceType);
enqueue(exclusion, parent, child, "<Java Local>", LOCAL);
} else {
enqueue(null, node, child, null, null);
}
}
大概看了下源码,结合第15小节的代码,就是图的最短路径问题了。在遍历过程中发现泄露对象即可结束,返回结果就是GcRoot到泄露对象的最短路径了。