LeakCanary 作为最简单直接的内存泄漏检测工具,非常受欢迎,github 上已经达到了 1.9 W star。官方地址如下:https://github.com/square/leakcanary
使用说明:
在debug 版本-设置-环境设置-LeakCanary Setting 查看是否打开了 LeakCanary。
执行你的日常开发和自测,目前支持 Activity 泄漏 和 window 泄漏检测。
每次Window 退出时,会进行泄漏检测。如果存在泄漏,会静止进程,dump 出 heap。如图1
在通知栏能看到泄漏的 WIndow,如图2,或者点击桌面图标-极速版内存泄漏,可以看到更多的泄漏信息,如图3
查看泄漏信息,根据具体的引用情况,解决内存泄漏。
极速版已经集成 LeakCanary 用于检测 Window 泄漏,具体植入代码:
gradle.build
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4'
也就是意味着,debug 和 release 依赖的包不同,而发布 release 包时,依赖 no-op 这个库,里面只有,两个类,只有五个方法,所以直接发布 release 包,对包大小基本没有影响。
LeakCanary 的 使用代码如下:
public class ExampleApplication extends Application {
public static RefWatcher getRefWatcher(Context context) {
ExampleApplication application = (ExampleApplication) context.getApplicationContext();
return application.refWatcher;
}
private RefWatcher refWatcher;
@Override public void onCreate() {
super.onCreate();
if (LeakCanary.isInAnalyzerProcess(this)) {
// 判断是否和 LeakCanary 初始化同一进程
return;
}
refWatcher = LeakCanary.install(this);//获取一个 Watcher
}
}
检测 Activity 泄漏的话,由于 LeakCanary install 的时候,会通过注入一个 Application.ActivityLifecycleCallbacks 去监听 Activity 的生命周期,所以,无限我们自己监听。
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 onActivityDestroyed(Activity activity) {
ActivityRefWatcher.this.onActivityDestroyed(activity);
}
};
比如你需要检测一个 Window 泄漏,你就在 Window 的 onDestory() 方法里面:
@Override public void onDestroy() {
super.onDestroy();
RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());
refWatcher.watch(this);
}
其实就是通过软引用,持有 this 当前对象,然后会触发一次 gc ,然后在检测当前this 是否为 null。
RefWatcher.java
public void watch(Object watchedReference, String referenceName) {
if (this == DISABLED) {
return;
}
final long watchStartNanoTime = System.nanoTime();
String key = UUID.randomUUID().toString();
retainedKeys.add(key);
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);//关键点1
ensureGoneAsync(watchStartNanoTime, reference);//关键方法
}
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
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();//关键点2 触发 GC
removeWeaklyReachableReferences();//关键点 3 移除被回收的对象
if (!gone(reference)) {//关键点 4 ,查看当前引用是否泄漏,如果泄漏,在后台service dump heap
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;
}
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);
}
}
结合代码,给出一些相关解释:
ReferenceQueue
联合使用时,如果弱引用持有的对象被垃圾回收,Java 虚拟机就会把这个弱引用加入到与之关联的引用队列中。即 KeyedWeakReference
持有的 Activity
对象如果被垃圾回收,该对象就会加入到引用队列 queue
中。