Squareup大神公司出品的著名android检测内存泄漏的库,这里不介绍用法。
官网地址https://github.com/square/leakcanary。
官方说明了实现原理。
1 crefWatcher.watch() reates a KeyedWeakReference to the watched object.
2 Later, in a background thread, it checks if the reference has been cleared and if not it triggers a GC.
3 If the reference is still not cleared, it then dumps the heap into a .hprof file stored on the app file system.
4 HeapAnalyzerService is started in a separate process and
HeapAnalyzer
parses the heap dump using HAHA.
5 HeapAnalyzer finds the keyedWeakReference in the heap dump thanks to a unique reference key and locates the leaking reference.
6 HeapAnalyzer computes the shortest strong reference path to the GC Roots to determine if there is a leak, and then builds the chain of references causing the leak.
7 The result is passed back to DisplayLeakService in the app process, and the leak notification is shown.
其实实现原理说的非常清楚了。
这篇文章看一看代码是怎么具体实现的。
从LeakCanary.install函数开始分析具体实现。
LeakCanary最简单的使用方式就是在application类调用LeakCanary.install(this).
LeakCanary.java
public static RefWatcher install(Application application) {
return install(application, DisplayLeakService.class,
AndroidExcludedRefs.createAppDefaults().build());
}
public static RefWatcher install(Application application,
Class<? extends AbstractAnalysisResultService> listenerServiceClass,
ExcludedRefs excludedRefs) {
if (isInAnalyzerProcess(application)) {
return RefWatcher.DISABLED;
}
enableDisplayLeakActivity(application);
HeapDump.Listener heapDumpListener =
new ServiceHeapDumpListener(application, listenerServiceClass);
RefWatcher refWatcher = androidWatcher(application, heapDumpListener, excludedRefs);
ActivityRefWatcher.installOnIcsPlus(application, refWatcher); //官方文档 ActivityRefWatcher负责检测activity leak
return refWatcher; //注意这里是函数返回值 复用refWatcher(这个类可以追踪任何类型的对象(不止是activity
}
看下androidWatcher函数的实现 其实就是一个普通的RefWatcher
public static RefWatcher androidWatcher(Context context, HeapDump.Listener heapDumpListener,
ExcludedRefs excludedRefs) {
DebuggerControl debuggerControl = new AndroidDebuggerControl();
AndroidHeapDumper heapDumper = new AndroidHeapDumper(context); // heapDumper这个类下文有分析 用来dumper(联想Eclipse的MAT工具
heapDumper.cleanup();
return new RefWatcher(new AndroidWatchExecutor(), debuggerControl, GcTrigger.DEFAULT,
heapDumper, heapDumpListener, excludedRefs); //这几个类在refWatcher的ensurego函数里被调用
}
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(); //可以看到installOnIcsPlus函数调用了watchActivites来监视activity
}
public void watchActivities() {
// Make sure you don't get installed twice.
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks); //activityRefWatcher监视activity的方法就是监听ActivityLifeCleCallback
}
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new Application.ActivityLifecycleCallbacks() {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override public void onActivityStarted(Activity activity) {
}
@Override public void onActivityResumed(Activity activity) {
}
@Override public void onActivityPaused(Activity activity) {
}
@Override public void onActivityStopped(Activity activity) {
}
@Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override public void onActivityDestroyed(Activity activity) {
ActivityRefWatcher.this.onActivityDestroyed(activity); //可以看到leak canary只在activity onDestroyed触发判断是否有内存泄漏
}
};
void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity); //这里的实现就是官方说的 先触发gc 若触发后仍然没有被clear说明存在内存泄漏
}
好,通过前面的分析我们看到leakcanary通过添加一个ActivityLifecleCallback的方法在activity onDestroy的时候触发判断是否有内存泄漏。
RefWatcher.java
public void watch(Object watchedReference) {
watch(watchedReference, "");
}
/**
* Watches the provided references and checks if it can be GCed. This method is non blocking,
* the check is done on the {@link Executor} this {@link RefWatcher} has been constructed with.
*
* @param referenceName An logical identifier for the watched object.
*/
public void watch(Object watchedReference, String referenceName) {
if (debuggerControl.isDebuggerAttached()) {
return;
}
final long watchStartNanoTime = System.nanoTime();
String key = UUID.randomUUID().toString();
retainedKeys.add(key);
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue); //使用特殊的技巧打上tag
watchExecutor.execute(new Runnable() {
@Override public void run() {
ensureGone(reference, watchStartNanoTime);
}
});
}
void ensureGone(KeyedWeakReference reference, long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
removeWeaklyReachableReferences(); //使用特殊的技巧remove weaklyrechable的tag
if (gone(reference) || debuggerControl.isDebuggerAttached()) {
return;
}
gcTrigger.runGc(); //触发gc
removeWeaklyReachableReferences();
//使用特殊的技巧remove weaklyrechable的tag
if (!gone(reference)) { //还没有remove 掉 说明发生内存泄漏
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
File heapDumpFile = heapDumper.dumpHeap();
if (heapDumpFile == HeapDumper.NO_DUMP) {
// Could not dump the heap, abort.
return;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
heapdumpListener.analyze(
//dump并分析
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
}
}
上文的注释中有分析dump的实现类是AndroidHeapDumper.
AndroidHeapDumper.java
@Override public File dumpHeap() {
if (!isExternalStorageWritable()) {
}
File heapDumpFile = getHeapDumpFile();
if (heapDumpFile.exists()) {
return NO_DUMP;
}
FutureResult<Toast> waitingForToast = new FutureResult<>();
showToast(waitingForToast);
if (!waitingForToast.wait(5, SECONDS)) {
return NO_DUMP;
}
Toast toast = waitingForToast.get();
try {
Debug.dumpHprofData(heapDumpFile.getAbsolutePath()); //dump heap 是由系统实现的
cancelToast(toast);
return heapDumpFile;
} catch (Exception e) {
cleanup();
return NO_DUMP;
}
}
ok~这篇文章就先到这里
这篇文章从LeakCanary.install(this)开始分析了leakCanary的关键实现原理。
比如,refWatcher类负责追踪一个对象在内存中是否存在。
refWatcher插入android系统是通过application的
Application.ActivityLifecycleCallbacks接口中的
onDestroy。
refWatcher追踪对象的实现手法是自己保存一个random key的key map,当发现activity对应的这个key
没有被remove时触发gc,gc后仍然存在,则调用android.os.debug api进行dump,然后分析dump出来
的文件。