目录
2.Fragment是如何进行内存泄漏检测的? 泄漏的判断标准是什么?
3.1 LeakCanary初始化流程 + 配置Activity泄漏监听(Fragment泄漏监听)
2.Fragment是如何进行内存泄漏检测的? 泄漏的判断标准是什么?
思考问题
1.LeakCanary 是如何实现内存泄漏检测的?
2.Fragment是如何进行内存泄漏检测的? 泄漏的判断标准是什么?
3.什么时候(时机)会进行内存泄漏检查?
4.内存快照文件是怎么生成的?
一、版本
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.2'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.2'
// 可选,如果你使用支持库的fragments的话
debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.2'
二、类图
三、流程
3.1 LeakCanary初始化流程 + 配置Activity泄漏监听(Fragment泄漏监听)
public class WanAndroidApp extends Application {
private RefWatcher refWatcher;
public static RefWatcher getRefWatcher(Context context) {
WanAndroidApp application = (WanAndroidApp) context.getApplicationContext();
return application.refWatcher;
}
@Override public void onCreate() {
super.onCreate();
//如果在内存分析进程中,则返回
if (LeakCanary.isInAnalyzerProcess(this)) {
return;
}
//主进程进行初始化
refWatcher = LeakCanary.install(this);
}
}
====
LeakCanary.java
/**
* Creates a {@link RefWatcher} that works out of the box, and starts watching activity
* references (on ICS+).
*/
public static @NonNull RefWatcher install(@NonNull Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
//补充1
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
return new AndroidRefWatcherBuilder(context);
}
====
AndrdoidRefWatcherBuilder.java
/**
* Sets a custom {@link AbstractAnalysisResultService} to listen to analysis results. This
* overrides any call to {@link #heapDumpListener(HeapDump.Listener)}.
*/
public @NonNull AndroidRefWatcherBuilder listenerServiceClass(
@NonNull Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
//创建一个内存dump的回调监听
return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
}
public @NonNull RefWatcher buildAndInstall() {
if (LeakCanaryInternals.installedRefWatcher != null) {
throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
}
//创建一个默认的RefWatcher (补充4)
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
//设置DisplayLeakActivity.class组件可用
LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
if (watchActivities) {
//添加Activity泄漏的监听 (补充2)
ActivityRefWatcher.install(context, refWatcher);
}
if (watchFragments) {
//添加Fragment泄漏的监听 (补充3)
FragmentRefWatcher.Helper.install(context, refWatcher);
}
}
//设置LeakCanaryInternals内部观察对象
LeakCanaryInternals.installedRefWatcher = refWatcher;
return refWatcher;
}
补充1
/**
* This returns the references in the leak path that can be ignored for app developers. This
* doesn't mean there is no memory leak, to the contrary. However, some leaks are caused by bugs
* in AOSP or manufacturer forks of AOSP. In such cases, there is very little we can do as app
* developers except by resorting to serious hacks, so we remove the noise caused by those leaks.
*/
public static @NonNull ExcludedRefs.Builder createAppDefaults() {
//排除Android系统泄漏和厂商泄漏的Case集合
return createBuilder(EnumSet.allOf(AndroidExcludedRefs.class));
}
public static @NonNull ExcludedRefs.Builder createBuilder(EnumSet<AndroidExcludedRefs> refs) {
ExcludedRefs.Builder excluded = ExcludedRefs.builder();
for (AndroidExcludedRefs ref : refs) {
//根据枚举类里面的applies属性,排除的系统泄漏和厂商的泄漏case
if (ref.applies) {
ref.add(excluded);
((ExcludedRefs.BuilderWithParams) excluded).named(ref.name());
}
}
return excluded;
}
补充2
ActivityRefWatcher.java
public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {
Application application = (Application) context.getApplicationContext();
ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
//注册Activity生命周期回调 application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
}
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityDestroyed(Activity activity) {
//当activity回调onDestroyed()时,使用refWatcher进行观察
refWatcher.watch(activity);
}
};
补充3
FragmentRefWatcher.java
public static void install(Context context, RefWatcher refWatcher) {
List<FragmentRefWatcher> fragmentRefWatchers = new ArrayList<>();
if (SDK_INT >= O) {
//添加Android O版本的Fragment
fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher));
}
try {
Class<?> fragmentRefWatcherClass = Class.forName(SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME);
Constructor<?> constructor =
fragmentRefWatcherClass.getDeclaredConstructor(RefWatcher.class);
FragmentRefWatcher supportFragmentRefWatcher =
(FragmentRefWatcher) constructor.newInstance(refWatcher);
//使用反射添加support库的Fragment
fragmentRefWatchers.add(supportFragmentRefWatcher);
} catch (Exception ignored) {
}
if (fragmentRefWatchers.size() == 0) {
return;
}
Helper helper = new Helper(fragmentRefWatchers);
Application application = (Application) context.getApplicationContext();
//注册Activity生命周期回调
application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks);
}
private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
//当Activity回调onCreate()时,遍历fragment观察集合
for (FragmentRefWatcher watcher : fragmentRefWatchers) {
watcher.watchFragments(activity);
}
}
};
====
SupportFragmentRefWatcher.java
@Override public void watchFragments(Activity activity) {
if (activity instanceof FragmentActivity) {
FragmentManager supportFragmentManager =
((FragmentActivity) activity).getSupportFragmentManager();
//注册activity对应的fragment的生命周期回调
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
}
}
private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
new FragmentManager.FragmentLifecycleCallbacks() {
@Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment fragment) {
View view = fragment.getView();
if (view != null) {
//当Fragment回调onViewDestroyed()时,进行refWatcher.watch()观察泄漏
refWatcher.watch(view);
}
}
@Override public void onFragmentDestroyed(FragmentManager fm, Fragment fragment) {
//当Fragment回调onDestroyed()时,进行refWatcher.watch()观察泄漏
refWatcher.watch(fragment);
}
};
补充4:由于Builder使用的是AndroidRefWatcherBuilder对象,默认的创建对象进行了重写,比如Executor,DebugControl...
RefWatcherBuilder.java
/** Creates a {@link RefWatcher}. */
public final RefWatcher build() {
if (isDisabled()) {
return RefWatcher.DISABLED;
}
//前面我们设置了排除Android原生和厂商泄漏的Case,所以excludedRefs != null
if (heapDumpBuilder.excludedRefs == null) {
heapDumpBuilder.excludedRefs(defaultExcludedRefs());
}
HeapDump.Listener heapDumpListener = this.heapDumpListener;
if (heapDumpListener == null) {
//设置内存dump回调监听
heapDumpListener = defaultHeapDumpListener();
}
//设置默认Debug控制器AndroidDebuggerControl,用来在用户Debug时,不进行内存观察!
DebuggerControl debuggerControl = this.debuggerControl;
if (debuggerControl == null) {
debuggerControl = defaultDebuggerControl();
}
HeapDumper heapDumper = this.heapDumper;
if (heapDumper == null) {
//设置默认内存dumper对象AndroidHeapDumper
heapDumper = defaultHeapDumper();
}
WatchExecutor watchExecutor = this.watchExecutor;
if (watchExecutor == null) {
//设置默认线程池AndroidWatchExecutor
watchExecutor = defaultWatchExecutor();
}
GcTrigger gcTrigger = this.gcTrigger;
if (gcTrigger == null) {
//设置默认的GcTrigger
gcTrigger = defaultGcTrigger();
}
if (heapDumpBuilder.reachabilityInspectorClasses == null) {
heapDumpBuilder.reachabilityInspectorClasses(defaultReachabilityInspectorClasses());
}
return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,
heapDumpBuilder);
}
3.2对象观察流程
在3.1最后一步的流程中,build()方法创建了一个RefWatcher对象
final class RefWatcher //最终类,不能继承
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 WatchExecutor} this {@link RefWatcher} has been constructed
* with.
*
* @param referenceName An logical identifier for the watched object.
*/
public void watch(Object watchedReference, String referenceName) {
if (this == DISABLED) {
return;
}
checkNotNull(watchedReference, "watchedReference");
checkNotNull(referenceName, "referenceName");
final long watchStartNanoTime = System.nanoTime();
//生成一个唯一值的id
String key = UUID.randomUUID().toString();
//添加到保存retainedKeys(Set)集合中
retainedKeys.add(key);
//创建一个弱引用对象,并且关联queue弱引用队列 (补充1)
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
ensureGoneAsync(watchStartNanoTime, reference);
}
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
//AndroidWatchExecutor中将Retryable放入到主线程的IdleHandler进行执行
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);
//取出弱引用队列对象,并对retainKeys集合进行移除
removeWeaklyReachableReferences();
//如果项目正在debug时,返回RETRY结果,在AndroidWatchExecutor中延后执行
if (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks.
return RETRY;
}
//如果retainedKeys集合不包含该弱引用对象了,则表示对象已经被回收了,不存在泄漏
if (gone(reference)) {
return DONE;
}
//主动触发一次GC回收
gcTrigger.runGc();
//再一次移除retainKeys集合弱引用对象
removeWeaklyReachableReferences();
//如果retainedKeys集合包含该弱引用对象,则存在泄漏
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 heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
.referenceName(reference.name)
.watchDurationMs(watchDurationMs)
.gcDurationMs(gcDurationMs)
.heapDumpDurationMs(heapDumpDurationMs)
.build();
heapdumpListener.analyze(heapDump);
}
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;
//循环遍历,将弱引用队列中的对象弹出,并将retainKeys集合中进行去除
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key);
}
}
private boolean gone(KeyedWeakReference reference) {
//如果retainedKeys集合不包含该弱引用对象了,则表示对象已经被回收了,不存在泄漏
return !retainedKeys.contains(reference.key);
}
补充1
final class KeyedWeakReference extends WeakReference<Object> {
//继承WeakReference类,
//有核心功能:当弱引用被GC回收时,会把弱引用对象加入到关联的队列中
KeyedWeakReference(Object referent, String key, String name,
ReferenceQueue<Object> referenceQueue) {
super(checkNotNull(referent, "referent"), checkNotNull(referenceQueue, "referenceQueue"));
this.key = checkNotNull(key, "key");
this.name = checkNotNull(name, "name");
}
}
3.3dump内存文件+泄漏分析流程
AndroidHeapDumper.java
public File dumpHeap() {
File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();
if (heapDumpFile == RETRY_LATER) {
return RETRY_LATER;
}
FutureResult<Toast> 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;
}
Notification.Builder builder = new Notification.Builder(context)
.setContentTitle(context.getString(R.string.leak_canary_notification_dumping));
Notification notification = LeakCanaryInternals.buildNotification(context, builder);
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
int notificationId = (int) SystemClock.uptimeMillis();
notificationManager.notify(notificationId, notification);
Toast toast = waitingForToast.get();
try {
//核心代码:dump内存文件的API
Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
cancelToast(toast);
notificationManager.cancel(notificationId);
return heapDumpFile;
} catch (Exception e) {
CanaryLog.d(e, "Could not dump heap");
// Abort heap dump
return RETRY_LATER;
}
}
====
ServiceHeapDumpListener.java
@Override public void analyze(@NonNull HeapDump heapDump) {
checkNotNull(heapDump, "heapDump");
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
====
HeapAnalyzerService.java
public static void runAnalysis(Context context, HeapDump heapDump,
Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
setEnabledBlocking(context, HeapAnalyzerService.class, true);
setEnabledBlocking(context, listenerServiceClass, true);
Intent intent = new Intent(context, HeapAnalyzerService.class);
intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
intent.putExtra(HEAPDUMP_EXTRA, heapDump);
//启动前台服务
ContextCompat.startForegroundService(context, intent);
}
@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);
}
四、问题
1.LeakCanary 是如何实现内存泄漏检测的?
答:
底层原理:
将弱引用(WeakReference)和引用队列(ReferenceQueue)关联起来
如果弱引用持有的对象被GC回收了,JVM就会把这个弱引用对象加入到与之关联的引用队列中
2.Fragment是如何进行内存泄漏检测的? 泄漏的判断标准是什么?
答:
1.Fragment对象是通过注册activity里面的fragmentManager或者SupportFragmentManager对Fragment的生命周期onViewDetoryed(),onDestroyed()回调进行检测的
2.判断的标准是,如果fragment调用了生命周期onViewDetoryed(),onDestroyed(),而在retainedKeys集合中,还存在的话,说明Fragment泄漏了
3.什么时候(时机)会进行内存泄漏检查?
答:
在AndroidWatchExecutor中将Retryable放入到主线程的IdleHandler进行执行,将内存检测方法推迟到了主线程的Handler Looper闲置时进行检查!
4.内存快照文件是怎么生成的?
答:
通过调用 Debug.dumpHprofData(heapDumpFile.getAbsolutePath()); //会写入到指定文件中
五、总结
优点 | 1.DebugImplement实现,可以在研发流程中观察到内存泄漏问题 |
2.优秀的内存泄漏结果展示 | |
缺点 | 1.由于对内存dump时会非常耗时,并且停住主线程,所以不能用于线上环境 |
六、学习到了什么
设计模式 | 组合模式 RefWatcher是一个final class, 是检测泄漏方法的核心类, 同时拥有内存泄漏的通用发放, AndroidRefWatcher,FragmentRefWatcher引用RefWatcher对象来实现对Activity,Fragment对象的内存泄漏监听 内部使用用KOOMInternal类进行实现总体流程的控制 |
构造者模式 RefWatcherBuilder 进行对实体对象进行创建 | |
设计 | 1.在AndroidWatchExecutor中将Retryable放入到主线程的IdleHandler进行执行,将内存检测方法推迟到了主线程的Handler Looper闲置时进行检查! |
2.底层原理: 将弱引用(WeakReference)和引用队列(ReferenceQueue)关联起来 如果弱引用持有的对象被GC回收了,JVM就会把这个弱引用对象加入到与之关联的引用队列中 |