介绍
内存泄漏检测工具,square公司出品,github地址https://github.com/square/leakcanary。
gradle引用:
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
使用
在自定义application中onCreat方法中调用下面代码。默认只是检测所有activity中的内存泄漏。如果想检测fragment、service等的内存泄漏,需要在相应的类的onDestory方法中调用refWatcher.watch(this)即可。然后在使用app的过程中,如果出现内存泄漏就会在leaks的app中listview展示出来,并且有相应的路径。
if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
LeakCanary.install(this);
问题
使用特别简单,但是想了想,有几个问题值得思考。
如何检测到所有activity的内存泄漏的?
内存泄漏检测机制是什么?
内存泄漏轨迹的生成过程 ?
源码分析
public static RefWatcher install(Application application) {
return refWatcher(application)//创建引用监听者
.listenerServiceClass(DisplayLeakService.class)//显示泄漏信息service
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
以上为install源码,不难发现buildAndInstall是一个build模式,一般build都是一些校验或者初始化。
public RefWatcher buildAndInstall() {
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
LeakCanary.enableDisplayLeakActivity(context);
ActivityRefWatcher.installOnIcsPlus((Application) context, refWatcher);
}
return refWatcher;
}
然后build之后创建一个RefWatcher对象。
public final RefWatcher build() {
if (isDisabled()) {
return RefWatcher.DISABLED;
}
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();
}
return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,
excludedRefs);
}
创建完对象之后,enableDisplayLeakActivity方法是否展示泄漏activity,最重要的是ActivityRefWatcher.installOnIcsPlus((Application) context, refWatcher)处理。
@TargetApi(ICE_CREAM_SANDWICH) public final class ActivityRefWatcher {
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();
}
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);
}
};
private final Application application;
private final RefWatcher refWatcher;
/**
* Constructs an {@link ActivityRefWatcher} that will make sure the activities are not leaking
* after they have been destroyed.
*/
public ActivityRefWatcher(Application application, final RefWatcher refWatcher) {
this.application = checkNotNull(application, "application");
this.refWatcher = checkNotNull(refWatcher, "refWatcher");
}
void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
public void watchActivities() {
// Make sure you don't get installed twice.
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
public void stopWatchingActivities() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);
}
}
installOnIcsPlus方法先判断版本<14 不能使用,然后调用创建ActivityRefWatcher对象,调用watchActivities方法,watchActivities里面就是将传入的application注册registerActivityLifecycleCallbacks用来获取这个application的所有activity的生命周期。然后在Application.ActivityLifecycleCallbacks回调中onActivityDestroyed生命周期里面调用ActivityRefWatcher.this.onActivityDestroyed(activity)方法,最后调用到refWatcher.watch(activity),接下来进入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创建一个key放入retainedKeys数组里面,添加弱引用的标识,KeyedWeakReference是继承WeakReference的类,一个弱引用的封装。看到
new KeyedWeakReference(watchedReference, key, referenceName, queue),是在创建一个弱引用,并放到ReferenceQueue引用队列中。KeyedWeakReference里面日常参数校验,以及调用WeakReference的super方法。
final class KeyedWeakReference extends WeakReference
接下来查看ensureGoneAsync方法。
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);
//清除此时已经到ReferenceQueue中的弱引用
removeWeaklyReachableReferences();
if (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks.
return RETRY;
}
//没有内存泄漏
if (gone(reference)) {
return DONE;
}
//gc处理
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);
//生成hprof文件并进行分析hprof文件
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
}
return DONE;
}
源码大概就是先清除弱引用的队列,然后查看引用是否存在队列中,
.if (gone(reference)) 用来判断某个 reference 的 key 是否仍在 retainedKeys 里,若不在,表示已回收,否则继续。然后在手动gc,之后再清除弱引用,在查看是否弱引用队列中是否存在,如果存在证明有内存泄露,就生成hprof文件并分析hprof文件。
removeWeaklyReachableReferences方法是清除此时已经到ReferenceQueue中的弱引用,gone方法是检测存放key的数组是否存在key。
private boolean gone(KeyedWeakReference reference) {
return !retainedKeys.contains(reference.key);
}
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);
}
}
File heapDumpFile = heapDumper.dumpHeap(),生成hprof文件的具体实现在AndroidHeapDumper类中。
public final class AndroidHeapDumper implements HeapDumper {
private final Context context;
private final LeakDirectoryProvider leakDirectoryProvider;
private final Handler mainHandler;
public AndroidHeapDumper(Context context, LeakDirectoryProvider leakDirectoryProvider) {
this.leakDirectoryProvider = leakDirectoryProvider;
this.context = context.getApplicationContext();
mainHandler = new Handler(Looper.getMainLooper());
}
@Override public File dumpHeap() {
File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();
if (heapDumpFile == RETRY_LATER) {
return RETRY_LATER;
}
FutureResult 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");
// Abort heap dump
return RETRY_LATER;
}
}
private void showToast(final FutureResult waitingForToast) {
mainHandler.post(new Runnable() {
@Override public void run() {
final Toast toast = new Toast(context);
toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
toast.setDuration(Toast.LENGTH_LONG);
LayoutInflater inflater = LayoutInflater.from(context);
toast.setView(inflater.inflate(R.layout.leak_canary_heap_dump_toast, null));
toast.show();
// Waiting for Idle to make sure Toast gets rendered.
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override public boolean queueIdle() {
waitingForToast.set(toast);
return false;
}
});
}
});
}
private void cancelToast(final Toast toast) {
mainHandler.post(new Runnable() {
@Override public void run() {
toast.cancel();
}
});
}
}
分析的具体实现在HeapAnalyzerService中,HeapAnalyzerService是一个IntentService。
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);
}
public HeapAnalyzerService() {
super(HeapAnalyzerService.class.getSimpleName());
}
@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);
}
}
总结
如何检测到所有activity的内存泄漏的?
通过application注册registerActivityLifecycleCallbacks,来监听说有activity的生命周期,并在onDestory中监听。
内存泄漏检测机制是什么?
若 Activity 被正常回收,就把activity使用KeyedWeakReference创建一个弱引用,引用它的 KeyedWeakReference 会被自动放入 ReferenceQueue 中。首先先移除不可达用引用,如果当前引用不存在,就不继续了,然后gc,再次移除不可达引用,如果当前引用不存在,就不继续了。如果两次移除后都没有被回收,就根据队列的弱引用,生成HeapDump信息。
内存泄漏轨迹的生成过程 ?
利用 heapDumper 把内存情况 dump 成文件,利用 HeapAnalyzer 对 dump 的内存情况进行分析并进一步确认,若确定发生泄漏,调用 DisplayLeakService发送通知。