简介
LeakCancary 是一个实时监控内存泄漏的开源框架,当检测到有内存泄漏时,会以通知的方式提示开发者当前发生了内存泄漏
原理
监听activity的生命周期,在Activity的onDestory方法中,开始监听activity对象, 通过将Activity包装到WeakReference中,被WeakReference包装过的Activity对象如果被回收,该WeakReference引用会被放到ReferenceQueue中,通过监测ReferenceQueue里面的内容就能检查到Activity是否能够被回收。其中最重要的两个对象为
Set<String> retainedKeys: 存放所有监控的Activity的key(值为通过uuid,唯一标识Activity)
ReferenceQueue<Object> queue:所有被回收的activity对象会存放到这个引用队列里面, 如果想要知道一个activity有没有内存泄漏,则只需要判断该activity在
具体使用步骤
1.在build.gradle中添加依赖
dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4'
}
- 在Application中初始化
public class FastAndroidApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
//监测内存泄漏
if (LeakCanary.isInAnalyzerProcess(this)) {
return;
}
LeakCanary.install(this);
}
至此,LeakCancary 的接入工作就完成了,是不是超简单?
源码解析
我们从入口函数开始分析
if (LeakCanary.isInAnalyzerProcess(this)) {
return;
}
LeakCanary.install(this);
首先判断当前运行的进程是否是属于HeapAnalyzerService进程,如果是 则return调,防止应用本身Application的onCreate方法多次初始化。这里需要这么做的原因是:LeakCancary本身是运行在另外一个进程中的,这点我们可以从LeakCancary的AndroidManifaset.xml中可以看出是有单独设置process Named的。
<activity
android:theme="@style/leak_canary_LeakCanary.Base"
android:name=".internal.DisplayLeakActivity"
android:process=":leakcanary"
android:enabled="false"
android:label="@string/leak_canary_display_activity_label"
android:icon="@mipmap/leak_canary_icon"
android:taskAffinity="com.squareup.leakcanary.${applicationId}"
>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
接下来执行 LeakCanary.install
# LeakCanary.java
public static @NonNull RefWatcher install(@NonNull Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
LeakCanary.refWatcher()
public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
return new AndroidRefWatcherBuilder(context);
}
通过builder模式构建了AndroidRefWatcherBuilder对象,然后通过AndroidRefWatcherBuilder对象设置了listenerServiceClass()用于绑定DisplayLeakService服务,该服务用来分析和显示内存泄漏信息的通知
AndroidRefWatchBuilder.buildAndInstall
#AndroidRefWatchBuilder.java
public @NonNull RefWatcher buildAndInstall() {
if (LeakCanaryInternals.installedRefWatcher != null) {
throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
}
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
if (enableDisplayLeakActivity) {
LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
}
if (watchActivities) {
ActivityRefWatcher.install(context, refWatcher);
}
if (watchFragments) {
FragmentRefWatcher.Helper.install(context, refWatcher);
}
}
LeakCanaryInternals.installedRefWatcher = refWatcher;
return refWatcher;
}
先调用AndroidRefWatchBuilder.buid() 构建一个RefWatcher对象
public final RefWatcher build() {
if (isDisabled()) {
return RefWatcher.DISABLED;
}
if (heapDumpBuilder.excludedRefs == null) {
heapDumpBuilder.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();
}
if (heapDumpBuilder.reachabilityInspectorClasses == null) {
heapDumpBuilder.reachabilityInspectorClasses(defaultReachabilityInspectorClasses());
}
return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,
heapDumpBuilder);
}
构建RefWatcher的参数有
- watchExecutor:在activity 的onDetstroy方法中开始执行内存泄漏检测
- DebuggerControl:判断是否处于debug模式,如果是则不检测
- gcTrigger:执行gc
- HeapDumper:dump内存泄漏处的heap信息,写入hprof文件
- heapDumpListener:dump内存泄漏信息完后的回调
- excludedRefs:排除可以忽略的泄漏路径
解析来是
if (enableDisplayLeakActivity) {
LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
}
LeakCancaryInternal.java
# LeakCancaryInternal
public static void setEnabledBlocking(Context appContext, Class<?> componentClass,
boolean enabled) {
ComponentName component = new ComponentName(appContext, componentClass);
PackageManager packageManager = appContext.getPackageManager();
int newState = enabled ? COMPONENT_ENABLED_STATE_ENABLED : COMPONENT_ENABLED_STATE_DISABLED;
// Blocks on IPC.
packageManager.setComponentEnabledSetting(component, newState, DONT_KILL_APP);
}
启动DisplayLeakActivity并显示应用图标,这个图标是LeakCancary这个应用的图标
接下来是执行 :
ActivityRefWatcher.install(context, refWatcher);
# ActivityRefWatcher
public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {
Application application = (Application) context.getApplicationContext();
ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
}
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
};
在Activity的onDestroy方法中执行RefWatcher.watch(activity)
RefWatcher.java
# RefWatcher.java
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);
}
这里需要注意几个变量:
- retainedKeys:set集合,代表每一个监控对象的唯一表示,值为通过uuid产生
- KeyedWeakReference:自定义的WeakReafernced对象,持有监控对象和其对应的key值
- queue:ReferenceQueue对象,和 KeyedWeakReference 配合使用
这里有一个知识点:弱引用和引用队列配合时,当弱引用持有的对象被垃圾回收,java虚拟机会把这个弱引用加入到与之关联的引用队列中。也就是说当activity被回收时,activity对象的引用就会被添加到ReferenceQueue这个引用队列中。
接下来是具体的内存泄漏判断过程
efWatcher.ensureGoneAsync
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
这里的watchExecutor 实现类是AndroidWatchExecutor
AndroidWatchExecutor.execute()
# AndroidWatchExecutor.java
public void execute(@NonNull Retryable retryable) {
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
waitForIdle(retryable, 0);
} else {
postWaitForIdle(retryable, 0);
}
}
private void postWaitForIdle(final Retryable retryable, final int failedAttempts) {
mainHandler.post(new Runnable() {
@Override public void run() {
waitForIdle(retryable, failedAttempts);
}
});
}
private void waitForIdle(final Retryable retryable, final int failedAttempts) {
// This needs to be called from the main thread.
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override public boolean queueIdle() {
postToBackgroundWithDelay(retryable, failedAttempts);
return false;
}
});
}
private void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) {
long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor);
long delayMillis = initialDelayMillis * exponentialBackoffFactor;
backgroundHandler.postDelayed(new Runnable() {
@Override public void run() {
Retryable.Result result = retryable.run();
if (result == RETRY) {
postWaitForIdle(retryable, failedAttempts + 1);
}
}
}, delayMillis);
}
这里是切换到主线程,当消息队列空闲时执行run方法, run方法实际执行的是RefWatcher中的ensureGone()
RefWatcher.ensureGone();
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();
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);
HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
.referenceName(reference.name)
.watchDurationMs(watchDurationMs)
.gcDurationMs(gcDurationMs)
.heapDumpDurationMs(heapDumpDurationMs)
.build();
heapdumpListener.analyze(heapDump);
}
return DONE;
- 首先通过removeWeaklyReachablereference来移除已经被回收的Activity引用()
private void removeWeaklyReachableReferences() {
KeyedWeakReference ref;
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key);
}
}
遍历ReferenceQueue列表中的对象(这些对象已经被回收), 判断对象是否存在当前activity的弱引用,存在则删除retainedKeys中的activity的key 值; 如果activity没有被回收,则不会添加到ReferenceQueue,也就不会从retainedKeys中移除
2.通过gone(reference)来判断当前弱引用对应的Activity是否存在于retainedKeys?如果不存在,则说明通过第一步的操作,已经移除了该引用的key值,直接返回即可。
3.如果第二部没有返回,说明retainedKeys还存在当前activity的引用(也就是改activity没有被添加到ReferenceQueue,没有被回收),则调用GcTigger.runGc方法运行GC.
- gc完成后再执行第一步,
- 然后再判断第2步是否被回收了,如果还没回收(gone(reference)返回false), 则认为是已经内存泄漏了,则开始执行内存泄漏结果处理
AndroidHeapDumper.dumpHeap()
File heapDumpFile = heapDumper.dumpHeap();
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
.referenceName(reference.name)
.watchDurationMs(watchDurationMs)
.gcDurationMs(gcDurationMs)
.heapDumpDurationMs(heapDumpDurationMs)
.build();
heapdumpListener.analyze(heapDump);
调用 File heapDumpFile = leakDirectoryProvider.newHeapDumpFile(); 新建hprof文件,然后调用Debug.dumpHprofData() 方法 dump 当前堆内存并写入刚才创建的文件。
然后调用heapdumpListener.analyze(heapDump)分析刚刚生成的heapDumpwen文件
这里的heapdumpListener 是ServiceHeapDumpListener
@Override public void analyze(@NonNull HeapDump heapDump) {
checkNotNull(heapDump, "heapDump");
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
调用HeapAnalyzerService开始启动HeapAnalyzerService 这个前台服务执行分析
HeapAnalyzerService.java
# heapAnalyzer.checkForLeak
@Override protected void onHandleIntentInForeground(@Nullable 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, this, heapDump.reachabilityInspectorClasses);
AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
heapDump.computeRetainedHeapSize);
AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
}
调用heapAnalyzer.checkForLeak 获取结果后,调用AbstractAnalysisResultService.sendResultToListener展示分析结果。具体是通过DisplayLeakService 来展示的。
public class DisplayLeakService extends AbstractAnalysisResultService {
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAP_DUMP_EXTRA);
AnalysisResult result = (AnalysisResult) intent.getSerializableExtra(RESULT_EXTRA);
try {
onHeapAnalyzed(heapDump, result);
} finally {
//noinspection ResultOfMethodCallIgnored
heapDump.heapDumpFile.delete();
}
protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) {
String leakInfo = leakInfo(this, heapDump, result, true);
CanaryLog.d("%s", leakInfo);
boolean resultSaved = false;
boolean shouldSaveResult = result.leakFound || result.failure != null;
if (shouldSaveResult) {
heapDump = renameHeapdump(heapDump);
resultSaved = saveResult(heapDump, result);
}
PendingIntent pendingIntent;
String contentTitle;
String contentText;
if (!shouldSaveResult) {
contentTitle = getString(R.string.leak_canary_no_leak_title);
contentText = getString(R.string.leak_canary_no_leak_text);
pendingIntent = null;
} else if (resultSaved) {
pendingIntent = DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey);
if (result.failure == null) {
String size = formatShortFileSize(this, result.retainedHeapSize);
String className = classSimpleName(result.className);
if (result.excludedLeak) {
contentTitle = getString(R.string.leak_canary_leak_excluded, className, size);
} else {
contentTitle = getString(R.string.leak_canary_class_has_leaked, className, size);
}
} else {
contentTitle = getString(R.string.leak_canary_analysis_failed);
}
contentText = getString(R.string.leak_canary_notification_message);
} else {
contentTitle = getString(R.string.leak_canary_could_not_save_title);
contentText = getString(R.string.leak_canary_could_not_save_text);
pendingIntent = null;
}
// New notification id every second.
int notificationId = (int) (SystemClock.uptimeMillis() / 1000);
showNotification(this, contentTitle, contentText, pendingIntent, notificationId);
afterDefaultHandling(heapDump, result, leakInfo);
}
最后会执行afterDefaultHandling方法,在这里我们可以自定义一些操作,例如上报泄漏信息给服务器
总结
LeakCancary主要是利用了弱引用 WeakReference 和 引用队列 ReferenceQueue的知识,当WeakReference中引用的对象被回收时,该引用会被添加到ReferenceQueue中,如果没有被回收,则不会添加到ReferenceQueue中。 所以可以通过检测ReferenceQueue是否存在activity的引用来判断activity是否存在泄漏导致没有回收。
LeakCanacary 监控内存泄漏主要流程如下:
- 监听Activity的onDestroy方法,将Activity包装到WeakReference和ReferenceQueue中,并将该Activity对应的key(通过uuid产生),添加到set<String>retainedKeys中
- 便利ReferenceQueue队列,将存在于队列中的activity对象,从retainedKeys中移除
- 判断retainedKeys是否存在该activity引用的key (第一步通过uuid产生,如果不存在:则说明没有内存泄漏,终止流程;如果存在:执行第4步
- run gc
- 执行第2步
- 执行第3步,如果activity的key存在于set<String>retainedKeys,则认为存在内存泄漏
- 生成hprof文件
- 启动HeapAnalyzerService分析泄漏信息
- 启动DisplayService显示通知














网友评论