博客结构
用简单通俗的话来记录自己对架构的理解
1.背景
对于DAU上亿的应用来讲,是无法在开发测试阶段,完全覆盖所有场景的。所以,线上的的性能监控就显得尤为重要。在大司,一般都有自己的APM(Application Performance Monitor),监控启动速度,崩溃、OOM、内存、卡顿等。对于内存,主要是内存泄漏和内存溢出等。针对APM,腾讯推出了QAPM内测版,其它大厂也都有自己的XAPM,对于其它公司,更多使用的是在开发测试阶段的LeakCanary,不过,原理是一样的,今天就以LeakCanary进行内存泄漏监控的分析。
2.原理解析
把原理图解白活明白,咱们再进入源码的海洋中。
(1).检测内存泄漏模块
(a)利用Application的Activity的onDestroy的回调
Application.registerActivityLifecycleCallback(new ActivityLifecycleCallback(0{})
(b)onDestory回调时
I 创建WeakReference,将ReferenceQueue和回收页面传入。
II 定期(5s)检查RefereceQueue是否有弱引用对象,
III RefereceQueue不存在WeakReferece引用对象,则手动触发GC(System.gc),再次检查RefereceQueue队列中是否存在WeakReferece引用对象。存在,则证明WeakReference引用的对象页面不存在内存泄漏,不存在,则证明存在内存泄漏。
对于上面的描述,实际上大家不禁有以下两个问题:
(1)WeakReference与页面存在什么关系?
在onDestroy监听回调时,将Activity/Fragment和ReferenceQueue对象传入。WeakReference会引用Activity/Fragment。
(2)ReferenceQueue与WeakReference存在什么关系?
GC时,执行了onDestroy且无可达性的页面弱引用会添加到ReferenceQueue。等待内存的回收。比如MainActivity执行了onDestroy,且没有强引用,则对应的WeakReference则会被添加到ReferenceQueue.
(2).dump Heap内存快照模块
利用Debug.dumpHprofData(String fileName)保存内存快照
(3).内存分析模块
耗时操作,独立进程:
在LeakCanary 2.0之前使用haha 进行分析
在LeakCanary 2.0后,使用XX分析
(4).分析通知推送/服务器上报模块
LeakCanary启动服务进行推送通知
3.源码解析
1.版本依赖
基于Leakcanary 1.6.0版本
(1)gradle依赖
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.1'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.1'
// Optional, if you use support library fragments:
debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.1'
(2)Application处理
if (LeakCanary.isInAnalyzerProcess(this)) {//1
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
LeakCanary.install(this);
2.源码解析
1.创建RefWatcher
/**
* Creates a {@link RefWatcher} that works out of the box, and starts watching activity
* references (on ICS+).
*/
public static RefWatcher install(Application application) {
return refWatcher(application) //建造者模式Builder
.listenerServiceClass(DisplayLeakService.class)//分析服务结果监听
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())//排除系统内部应用开发者无法处理存泄漏索引
.buildAndInstall();//创建RefWatcher
}
2.buildAndInstall的分析
public final RefWatcher build() {
//
if (isDisabled()) {
return RefWatcher.DISABLED;
}
//构建HeapDump建造者模式,对dump文件进行管理
if (heapDumpBuilder.excludedRefs == null) {
heapDumpBuilder.excludedRefs(defaultExcludedRefs());
}
//HeapDump文件的监听
HeapDump.Listener heapDumpListener = this.heapDumpListener;
if (heapDumpListener == null) {
heapDumpListener = defaultHeapDumpListener();
}
//指向页面的弱引用是否是被添加到了队列,从而决定是否再次GC
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();
}
//GC的处理,使用的是Runtime.getRuntime().gc
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);
}
3.RefWatcher的分析
RefWatcher是个核心类,尤其是watch方法,现在,让我们看下它的真面目
/**
* 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();
String key = UUID.randomUUID().toString();
retainedKeys.add(key);
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
//弱引用指向的对象确认是否被回收
ensureGoneAsync(watchStartNanoTime, reference);
}
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);
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);
//进行内存泄漏的dump文件获取
HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
.referenceName(reference.name)
.watchDurationMs(watchDurationMs)
.gcDurationMs(gcDurationMs)
.heapDumpDurationMs(heapDumpDurationMs)
.build();
//解析内存泄漏的文件
heapdumpListener.analyze(heapDump);
}
return DONE;
}
4.问题答疑
1.LeakCanary监听不实时?
下载和解析dump文件是个异步耗时工作,只要是有权限,会在通知栏中进行dump文件的下载和分析实时展示。
2.LeakCanary是major gc还是full gc?
LeakCanary使用的是Runtime.getRuntime().gc(),它是针对老年代和年青代的full gc!
3.Fragment如何实现的监听回调。
有同学说,在如下代码中看到了Activity.onDestroy是在Application进行注册监听,但是,Fragment在Application中并没有,那么,它是如何注册的呢?让我们来look!
public static void install(Context context, RefWatcher refWatcher) {
Application application = (Application) context.getApplicationContext();
ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
//利用Application的注册 application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
}
//FragmentManager的注册
@Override public void watchFragments(Activity activity) {
FragmentManager fragmentManager = activity.getFragmentManager();
fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
}
5.站在巨人的肩膀
1.腾讯QAPM的内测申请
https://cloud.tencent.com/product/qapm/getting-started
2.腾讯QAPM的原理
https://cloud.tencent.com/developer/article/1385928
3.源码解析参考
https://blog.csdn.net/braintt/article/details/99685243
https://www.jianshu.com/p/261e70f3083f