参考:https://carsonho.blog.csdn.net/article/details/79407707
https://allenwu.itscoder.com/leakcanary-source
内存泄漏:
长期存活对象引用短期存活对象,导致短期存活对象占用的内存无法回收,这种现象称为内存泄漏。
内存溢出:
应用程序所需的内存超出了系统为其分配的内存限额的现象
常见的内存泄漏
-
集合类
-
原因
集合类添加元素后,仍引用着集合元素对象,导致该集合元素对象不可被回收,从而导致内存泄漏
-
实例
//通过 循环申请Object 对象 & 将申请的对象逐个放入到集合List List<Object> objectList = new ArrayList<>(); for (int i = 0; i < 10; i++) { Object o = new Object(); objectList.add(o); o = null; } //虽释放了集合元素引用的本身:o=null) //但集合List仍然引用该对象,故垃圾回收器GC 依然不可回收该对象
-
解决
//集合类 添加集合元素对象后,在使用后必须从集合中删除 objectList.clear(); objectList=null;
-
-
static关键字修饰的成员变量
-
被 Static 关键字修饰的成员变量的生命周期 = 应用程序的生命周期
-
原因
若使被 Static 关键字修饰的成员变量 引用耗费资源过多的实例(如Context),则容易出现该成员变量的生命周期 > 引用实例生命周期的情况,当引用实例需结束生命周期销毁时,会因静态变量的持有而无法被回收,从而出现内存泄
-
实例
// 创建单例时,需传入一个Context // 若传入的是Activity的Context,此时单例 则持有该Activity的引用 // 由于单例一直持有该Activity的引用(直到整个应用生命周期结束),即使该Activity退出,该Activity的内存也不会被回收 // 特别是一些庞大的Activity,此处非常容易导致OOM public class SingleInstanceClass { private static SingleInstanceClass instance; private Context mContext; private SingleInstanceClass(Context context) { this.mContext = context; // 传递的是Activity的context } public SingleInstanceClass getInstance(Context context) { if (instance == null) { instance = new SingleInstanceClass(context); } return instance; } } //单例模式 由于其静态特性,其生命周期的长度 = 应用程序的生命周期 //若1个对象已不需再使用 而单例对象还持有该对象的引用,那么该对象将不能被正常回收 从而 导致内存泄漏
-
解决
单例模式应该引用生命周期等于应用生命周期的对象。
public class SingleInstanceClass { private static SingleInstanceClass instance; private Context mContext; private SingleInstanceClass(Context context) { this.mContext = context.getApplicationContext(); // 传递的是Application 的context } public SingleInstanceClass getInstance(Context context) { if (instance == null) { instance = new SingleInstanceClass(context); } return instance; } }
-
-
非静态内部类/匿名类
非静态内部类/匿名类默认持有外部类的引用;而静态内部类则不会
常见3种情况,分别是:非静态内部类的实例 = 静态、多线程、消息传递机制(Handler)
-
非静态内部类的实例 = 静态
-
原因
若 非静态内部类所创建的实例 = 静态(其生命周期 = 应用的生命周期),会因 非静态内部类默认持有外部类的引用 而导致外部类无法释放,最终 造成内存泄露
-
实例
// 背景: // a. 在启动频繁的Activity中,为了避免重复创建相同的数据资源,会在Activity内部创建一个非静态内部类的单例 // b. 每次启动Activity时都会使用该单例的数据 public class TestActivity extends AppCompatActivity { // 非静态内部类的实例的引用 // 注:设置为静态 public static InnerClass innerClass = null; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 保证非静态内部类的实例只有1个 if (innerClass == null) innerClass = new InnerClass(); } // 非静态内部类的定义 private class InnerClass { //... } } // 造成内存泄露的原因: // a. 当TestActivity销毁时,因非静态内部类单例的引用(innerClass)的生命周期 = 应用App的生命周期、持有外部类TestActivity的引用 // b. 故 TestActivity无法被GC回收,从而导致内存泄漏
-
解决:
- 将非静态内部类设置为:静态内部类(静态内部类默认不持有外部类的引用)
- 该内部类抽取出来封装成一个单例
- 尽量 避免 非静态内部类所创建的实例 = 静态
ps:若需使用Context,建议使用 Application 的 Context
-
-
多线程:AsyncTask、实现Runnable接口、继承Thread类
-
原因
多线程的使用方法 = 非静态内部类 / 匿名类;即线程类属于非静态内部类 / 匿名类
当工作线程正在处理任务,外部类需销毁时,由于工作线程实例持有外部类引用,将使得外部类无法被垃圾回收器(GC)回收,从而造成内存泄露 -
实例
/** * 方式1:新建Thread子类(内部类) */ public class MainActivity extends AppCompatActivity { public static final String TAG = "carson:"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 通过创建的内部类 实现多线程 new MyThread().start(); } // 自定义的Thread子类 private class MyThread extends Thread{ @Override public void run() { try { Thread.sleep(5000); Log.d(TAG, "执行了多线程"); } catch (InterruptedException e) { e.printStackTrace(); } } } } /** * 方式2:匿名Thread内部类 */ public class MainActivity extends AppCompatActivity { public static final String TAG = "carson:"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 通过匿名内部类 实现多线程 new Thread() { @Override public void run() { try { Thread.sleep(5000); Log.d(TAG, "执行了多线程"); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); } } /** * 分析:内存泄露原因 */ // 工作线程Thread类属于非静态内部类 / 匿名内部类,运行时默认持有外部类的引用 // 当工作线程运行时,若外部类MainActivity需销毁 // 由于此时工作线程类实例持有外部类的引用,将使得外部类无法被垃圾回收器(GC)回收,从而造成 内存泄露
-
解决方案1:静态内部类不默认持有外部类的引用,从而使得 “工作线程实例 持有外部类引用” 的引用关系 不复存在将Thread的子类设置成静态内部类
/** * 解决方式1:静态内部类 * 原理:静态内部类 不默认持有外部类的引用,从而使得 “工作线程实例 持有 外部类引用” 的引用关系 不复存在 * 具体实现:将Thread的子类设置成 静态内部类 */ public class MainActivity extends AppCompatActivity { public static final String TAG = "carson:"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 通过创建的内部类 实现多线程 new MyThread().start(); } // 分析1:自定义Thread子类 // 设置为:静态内部类 private static class MyThread extends Thread{ @Override public void run() { try { Thread.sleep(5000); Log.d(TAG, "执行了多线程"); } catch (InterruptedException e) { e.printStackTrace(); } } } } /** * 解决方案2:当外部类结束生命周期时,强制结束线程 * 原理:使得 工作线程实例的生命周期 与 外部类的生命周期 同步 * 具体实现:当 外部类(此处以Activity为例) 结束生命周期时(此时系统会调用onDestroy()),强制结束线程(调用stop()) */ @Override protected void onDestroy() { super.onDestroy(); Thread.stop(); // 外部类Activity生命周期结束时,强制结束线程 }
-
解决方案2:使得工作线程实例的生命周期 外部类的生命周期同步
当外部类(此处以Activity为例) 结束生命周期时(此时系统会调用onDestroy(),强制结束线程(调用stop())
@Override protected void onDestroy() { super.onDestroy(); Thread.stop(); // 外部类Activity生命周期结束时,强制结束线程 }
-
-
Handler机制
-
解决方案1
静态内部类+弱引用
private static class FHandler extends Handler{ // 定义 弱引用实例 private WeakReference<Activity> reference; // 在构造方法中传入需持有的Activity实例 public FHandler(Activity activity) { // 使用WeakReference弱引用持有Activity实例 reference = new WeakReference<Activity>(activity); } // 通过复写handlerMessage() 从而确定更新UI的操作 @Override public void handleMessage(Message msg) { switch (msg.what) { case 1: Log.d(TAG, "收到线程1的消息"); break; case 2: Log.d(TAG, " 收到线程2的消息"); break; } } } //要使用静态内部类,不然可能会造成内存泄露。原因是非静态内部类会持有外部类的引用,而Handler发出的Message会持有Handler的引用。如果这个Message是个延迟的消息,此时activity被退出了,但Message依然在“流水线”上,Message->handler->activity(可达性分析法),那么activity就无法被回收,导致内存泄露。
-
解决方案2
当外部类结束生命周期时,清空Handler内消息队列
@Override protected void onDestroy() { super.onDestroy(); mHandler.removeCallbacksAndMessages(null); // 外部类Activity生命周期结束时,同时清空消息队列 & 结束Handler生命周期 }
-
-
资源使用后未关闭
-
原因:
对于资源的使用(如广播BraodcastReceiver、文件流File、数据库游标Cursor、图片资源Bitmap等),若在Activity销毁时无及时关闭/注销这些资源,则这些资源将不会被回收,从而造成内存泄漏。
-
解决:
// 对于 广播BraodcastReceiver:注销注册 unregisterReceiver() // 对于 文件流File:关闭流 InputStream / OutputStream.close() // 对于数据库游标cursor:使用后关闭游标 cursor.close() // 对于 图片资源Bitmap:Android分配给图片的内存只有8M,若1个Bitmap对象占内存较多,当它不再被使用时,应调用recycle()回收此对象的像素所占用的内存;最后再赋为null Bitmap.recycle(); Bitmap = null; // 对于动画(属性动画) // 将动画设置成无限循环播放repeatCount = “infinite”后 // 在Activity退出时记得停止动画
-
-
其他
图片已经损坏 :<
-
LeakCanary
参考:https://zhuanlan.zhihu.com/p/57425510
官方原理解释
正常情况下一个Activity在执行Destroy之后就要销毁,LeakCanary做的就是在一个Activity 被Destroy之后将它放在一个WeakReference中,然后将这个WeakReference关联到一个ReferenceQueue,查看ReferenceQueue队列是否存在这个Activity的引用,如果不在这个队列中,执行一些GC清洗操作,再次查看。如果仍然不存在则证明该Activity泄漏了,之后Dump出heap信息,并用haha这个开源库去分析泄漏路径。
基本原理
-
LeakCanary通过 install() 方法初始化,主要是构造出一个RefWatcher,来监听Activity(ActivityRefWatcher) 或者Fragment(FragmentRefWatcher)
-
监听Actvity,原理在于 Application 的registerActivityLifecycleCallbacks(),该方法可以对应用内所有 Activity 的生命周期做监听, LeakCanary只监听了Destroy()方法。
在每个Activity的OnDestroy()方法中都会回调refWatcher.watch()方法。 -
watch()方法中先随机生成一个数作为key放在一个叫retainedKeys的set容器里面,用来区分待分析对象是否被回收。将弱引用和引用队列ReferenceQueue联合使用,如果弱引用持有的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
即 KeyedWeakReference持有的Activity对象如果被垃圾回收,该对象就会加入到引用队列中。 -
避免因为gc不及时带来的误判,leakcanay会进行二次确认进行保证。首先清除此时已经到 RQ 的弱引用,调用 removeWeaklyReachableReferences()
把已被回收的对象的 key 从 retainedKeys 移除,剩下的 key 都是未被回收的对象。 -
通过gone(reference)判断当前弱引用对应的Activity是否已经被回收,如果已经回收说明activity没有泄漏,直接返回即可。
-
如果当前检测对象没有改变其可达状态,则进行手动GC,再次清除已经到 RQ的弱引用,调用 removeWeaklyReachableReferences();如果此时对象还没有到队列,则此时已经可能泄漏。
-
将heap信息dump出来,抓取dump文件。
-
HeapAnalyzerService被开启在一个独立的进程中,并且HeapAnalyzer使用了HAHA开源库解析了指定时刻的堆栈快照文件heap dump。
-
从heap dump中,HeapAnalyzer根据一个独特的引用key找到了KeyedWeakReference,并且定位了泄露的引用。
-
HeapAnalyzer为了确定是否有泄露,计算了到GC Roots的最短强引用路径,然后建立了导致泄露的链式引用。
-
这个结果被传回到app进程中的DisplayLeakService,然后一个泄露通知便展现出来了。
源码分析(版本二更深入)
版本一
创建RefWatcher
public final class LeakCanary {
public static @NonNull RefWatcher install(@NonNull Application application) {
return refWatcher(application) // 创建AndroidRefWatcherBuilder对象
.listenerServiceClass(DisplayLeakService.class) // 配置监听分析结果的服务
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build()) // 配置排除的系统泄露
.buildAndInstall(); // 创建一个Refwatcher并监听Activity的引用
}
// ...
}
AndroidRefWatcherBuilder#buildAndInstall
public final class AndroidRefWatcherBuilder extends RefWatcherBuilder<AndroidRefWatcherBuilder> {
public @NonNull RefWatcher buildAndInstall() {
if (LeakCanaryInternals.installedRefWatcher != null) {
throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
}
// 创建RefWatcher对象
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
if (enableDisplayLeakActivity) {
LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
}
if (watchActivities) {
// 监听Activity的引用
ActivityRefWatcher.install(context, refWatcher);
}
if (watchFragments) {
// 监听Fragment的引用
FragmentRefWatcher.Helper.install(context, refWatcher);
}
}
LeakCanaryInternals.installedRefWatcher = refWatcher;
return refWatcher;
}
// ...
}
监听Activity的引用
ActivityRefWatcher
public final class 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) {
// 在Activity执行完onDestroyed方法时,调用RefWatcher的watch来监控该Activity是否泄露
refWatcher.watch(activity);
}
};
// ...
}
检查引用
public final class RefWatcher {
public static final RefWatcher DISABLED = new RefWatcherBuilder<>().build();
// 线程控制器,在 onDestroy() 之后并且主线程空闲时执行内存泄漏检测
private final WatchExecutor watchExecutor;
// 判断是否处于调试模式,调试模式中不会进行内存泄漏检测,因为在调试过程中可能会保留上一个引用从而导致错误信息上报。
private final DebuggerControl debuggerControl;
// 用于主动触发GC操作
private final GcTrigger gcTrigger;
// 堆信息转储者,dump 内存泄漏处的 heap 信息到 hprof 文件
private final HeapDumper heapDumper;
private final HeapDump.Listener heapdumpListener;
private final HeapDump.Builder heapDumpBuilder;
// 保存每个被检测对象所对应的唯一key
private final Set<String> retainedKeys;
// 引用队列,和WeakReference配合使用,当弱引用所引用的对象被GC回收,该弱引用就会被加入到这个队列
private final ReferenceQueue<Object> queue;
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();
// 为被检测对象生成唯一的key值,并保存到retainedKeys
String key = UUID.randomUUID().toString();
retainedKeys.add(key);
// 创建被检测对象的弱引用,并传入该对象的key
final KeyedWeakReference reference = new KeyedWeakReference(watchedReference, key, referenceName, queue);
// 异步检测这个对象是否被回收
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);
}
});
}
@SuppressWarnings("ReferenceEquality") // Explicitly checking for named null.
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();
// GC后再移除对象已经被回收的弱引用
removeWeaklyReachableReferences();
// 如果该引用还存在,就表示对象已经泄露
if (!gone(reference)) {
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
// dump出heap的内存快照
File heapDumpFile = heapDumper.dumpHeap();
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
// 构建HeapDump对象
HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
.referenceName(reference.name)
.watchDurationMs(watchDurationMs)
.gcDurationMs(gcDurationMs)
.heapDumpDurationMs(heapDumpDurationMs)
.build();
// 分析HeapDump对象
heapdumpListener.analyze(heapDump);
}
return DONE;
}
private boolean gone(KeyedWeakReference reference) {
return !retainedKeys.contains(reference.key);
}
private void removeWeaklyReachableReferences() {
KeyedWeakReference ref;
// 当弱引用所引用的对象被回收,就会把该引用放到queue中,所以可以通过queue来判断对象是否被回收
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key);
}
}
// ...
}
Dump Heap
AndroidHeapDumper是HeapDumper的实现类。
public final class AndroidHeapDumper implements HeapDumper {
@Override @Nullable
public File dumpHeap() {
File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();
if (heapDumpFile == RETRY_LATER) {
return RETRY_LATER;
}
// ...
try {
// 生成.hprof文件
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;
}
}
// ...
}
解析hprof
public final class ServiceHeapDumpListener implements HeapDump.Listener {
// ...
@Override
public void analyze(@NonNull HeapDump heapDump) {
checkNotNull(heapDump, "heapDump");
// 启动HeapAnalyzerServiceService来分析heapDump
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
}
public final class HeapAnalyzerService extends ForegroundService implements AnalyzerProgressListener {
// ...
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);
}
}
public final class HeapAnalyzer {
// ...
public @NonNull AnalysisResult checkForLeak(@NonNull File heapDumpFile, @NonNull String referenceKey, boolean computeRetainedSize) {
long analysisStartNanoTime = System.nanoTime();
if (!heapDumpFile.exists()) {
Exception exception = new IllegalArgumentException("File does not exist: " + heapDumpFile);
return failure(exception, since(analysisStartNanoTime));
}
try {
listener.onProgressUpdate(READING_HEAP_DUMP_FILE);
// 使用haha库解析.hprof文件
HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
HprofParser parser = new HprofParser(buffer);
listener.onProgressUpdate(PARSING_HEAP_DUMP);
// 解析.hprof文件生成对应的快照对象
Snapshot snapshot = parser.parse();
listener.onProgressUpdate(DEDUPLICATING_GC_ROOTS);
// 删除gcRoots中重复的根对象RootObj
deduplicateGcRoots(snapshot);
listener.onProgressUpdate(FINDING_LEAKING_REF);
// 检查对象是否泄露
Instance leakingRef = findLeakingReference(referenceKey, snapshot);
// leakingRef为空表示对象没有泄露
if (leakingRef == null) {
String className = leakingRef.getClassObj().getClassName();
return noLeak(className, since(analysisStartNanoTime));
}
// 查找引用链
return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize);
} catch (Throwable e) {
return failure(e, since(analysisStartNanoTime));
}
}
}
定位泄露的引用
public final class HeapAnalyzer {
// ...
private Instance findLeakingReference(String key, Snapshot snapshot) {
ClassObj refClass = snapshot.findClass(KeyedWeakReference.class.getName());
if (refClass == null) {
throw new IllegalStateException(
"Could not find the " + KeyedWeakReference.class.getName() + " class in the heap dump.");
}
List<String> keysFound = new ArrayList<>();
for (Instance instance : refClass.getInstancesList()) {
List<ClassInstance.FieldValue> values = classInstanceValues(instance);
Object keyFieldValue = fieldValue(values, "key");
if (keyFieldValue == null) {
keysFound.add(null);
continue;
}
String keyCandidate = asString(keyFieldValue);
if (keyCandidate.equals(key)) {
return fieldValue(values, "referent");
}
keysFound.add(keyCandidate);
}
throw new IllegalStateException("Could not find weak reference with key " + key + " in " + keysFound);
}
}
建立引用链
public final class HeapAnalyzer {
// ...
private AnalysisResult findLeakTrace(long analysisStartNanoTime, Snapshot snapshot, Instance leakingRef, boolean computeRetainedSize) {
listener.onProgressUpdate(FINDING_SHORTEST_PATH);
// 查找到GC Roots的最短引用路径
ShortestPathFinder pathFinder = new ShortestPathFinder(excludedRefs);
ShortestPathFinder.Result result = pathFinder.findPath(snapshot, leakingRef);
String className = leakingRef.getClassObj().getClassName();
// False alarm, no strong reference path to GC Roots.
if (result.leakingNode == null) {
return noLeak(className, since(analysisStartNanoTime));
}
listener.onProgressUpdate(BUILDING_LEAK_TRACE);
// 构建泄露的引用链
LeakTrace leakTrace = buildLeakTrace(result.leakingNode);
long retainedSize;
if (computeRetainedSize) {
listener.onProgressUpdate(COMPUTING_DOMINATORS);
// 计算内存泄露的大小
snapshot.computeDominators();
Instance leakingInstance = result.leakingNode.instance;
retainedSize = leakingInstance.getTotalRetainedSize();
// TODO: check O sources and see what happened to android.graphics.Bitmap.mBuffer
if (SDK_INT <= N_MR1) {
listener.onProgressUpdate(COMPUTING_BITMAP_SIZE);
retainedSize += computeIgnoredBitmapRetainedSize(snapshot, leakingInstance);
}
} else {
retainedSize = AnalysisResult.RETAINED_HEAP_SKIPPED;
}
return leakDetected(result.excludingKnownLeaks, className, leakTrace, retainedSize, since(analysisStartNanoTime));
}
}
展示分析结果
public class DisplayLeakService extends AbstractAnalysisResultService {
// ...
@Override
protected final void onHeapAnalyzed(@NonNull AnalyzedHeap analyzedHeap) {
HeapDump heapDump = analyzedHeap.heapDump;
AnalysisResult result = analyzedHeap.result;
String leakInfo = leakInfo(this, heapDump, result, true);
CanaryLog.d("%s", leakInfo);
heapDump = renameHeapdump(heapDump);
boolean resultSaved = saveResult(heapDump, result);
String contentTitle;
if (resultSaved) {
PendingIntent pendingIntent = DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey);
if (result.failure != null) {
contentTitle = getString(R.string.leak_canary_analysis_failed);
} else {
String className = classSimpleName(result.className);
// ...
}
String contentText = getString(R.string.leak_canary_notification_message);
showNotification(pendingIntent, contentTitle, contentText);
} else {
onAnalysisResultFailure(getString(R.string.leak_canary_could_not_save_text));
}
afterDefaultHandling(heapDump, result, leakInfo);
}
@Override
protected final void onAnalysisResultFailure(String failureMessage) {
super.onAnalysisResultFailure(failureMessage);
String failureTitle = getString(R.string.leak_canary_result_failure_title);
showNotification(null, failureTitle, failureMessage);
}
}
总结
img版本二
-
LeakCanary#install()
public static @NonNull RefWatcher install(@NonNull Application application) { return refWatcher(application)// 创建AndroidRefWatcherBuilder对象 .listenerServiceClass(DisplayLeakService.class)// 配置监听分析结果的服务 .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())// 配置排除的系统泄露 .buildAndInstall();// 创建一个Refwatcher并监听Activity的引用 }
在install()方法中,可以理解为如下4步骤
- refWatcher(application)
- 链式调用listenerServiceClass(DisplayLeakService.class)
- 链式调用excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
- 链式调用buildAndInstall()
首先,我们来看下第一步,这里调用了LeakCanary类的refWatcher方法,如下所示:
public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) { return new AndroidRefWatcherBuilder(context); }
然后新建了一个AndroidRefWatcherBuilder对象,再看看AndroidRefWatcherBuilder这个类。
-
AndroidRefWatcherBuilder
/** A {@link RefWatcherBuilder} with appropriate Android defaults. */ public final class AndroidRefWatcherBuilder extends RefWatcherBuilder<AndroidRefWatcherBuilder> { ... AndroidRefWatcherBuilder(@NonNull Context context) { this.context = context.getApplicationContext(); } ... }
在AndroidRefWatcherBuilder的构造方法中仅仅是将外部传入的applicationContext对象保存起来了。AndroidRefWatcherBuilder是一个适配Android平台的引用观察者构造器对象,它继承了RefWatcherBuilder,RefWatcherBuilder是一个负责建立引用观察者RefWatcher实例的基类构造器。继续看看RefWatcherBuilder这个类。
-
RefWatchBuilder
public class RefWatcherBuilder<T extends RefWatcherBuilder<T>> { ... public RefWatcherBuilder() { heapDumpBuilder = new HeapDump.Builder(); } ... }
在RefWatcher的基类构造器RefWatcherBuilder的构造方法中新建了一个HeapDump的构造器对象。其中HeapDump就是一个保存heap dump信息的数据结构。
接着来分析下install()方法中的链式调用的listenerServiceClass(DisplayLeakService.class)这部分逻辑。
-
AndroidRefWatcherBuilder#listenerServiceClass()
public @NonNull AndroidRefWatcherBuilder listenerServiceClass( @NonNull Class<? extends AbstractAnalysisResultService> listenerServiceClass) { return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass)); }
在这里,传入了一个DisplayLeakService的Class对象,它的作用是展示泄露分析的结果日志,然后会展示一个用于跳转到显示泄露界面DisplayLeakActivity的通知。在listenerServiceClass()这个方法中新建了一个ServiceHeapDumpListener对象,看看它内部的操作。
-
ServiceHeapDumpListener
public final class ServiceHeapDumpListener implements HeapDump.Listener { ... public ServiceHeapDumpListener(@NonNull final Context context, @NonNull final Class<? extends AbstractAnalysisResultService> listenerServiceClass) { this.listenerServiceClass = checkNotNull(listenerServiceClass, "listenerServiceClass"); this.context = checkNotNull(context, "context").getApplicationContext(); } ... }
可以看到这里仅仅是在ServiceHeapDumpListener中保存了DisplayLeakService的Class对象和application对象。它的作用就是接收一个heap dump去分析。
然后我们继续看install()方法链式调用.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())的这部分代码。先看AndroidExcludedRefs.createAppDefaults()。
-
AndroidExcludedRefs#createAppDefaults()
public enum AndroidExcludedRefs { ... public static @NonNull ExcludedRefs.Builder createAppDefaults() { return createBuilder(EnumSet.allOf(AndroidExcludedRefs.class)); } public static @NonNull ExcludedRefs.Builder createBuilder(EnumSet<AndroidExcludedRefs> refs) { ExcludedRefs.Builder excluded = ExcludedRefs.builder(); for (AndroidExcludedRefs ref : refs) { if (ref.applies) { ref.add(excluded); ((ExcludedRefs.BuilderWithParams) excluded).named(ref.name()); } } return excluded; } ... }
先来说下AndroidExcludedRefs这个类,它是一个enum类,它声明了Android SDK和厂商定制的SDK中存在的内存泄露的case,根据AndroidExcludedRefs这个类的类名就可看出这些case都会被Leakcanary的监测过滤掉。目前这个版本是有46种这样的case被包含在内,后续可能会一直增加。然后EnumSet.allOf(AndroidExcludedRefs.class)这个方法将会返回一个包含AndroidExcludedRefs元素类型的EnumSet。Enum是一个抽象类,在这里具体的实现类是通用正规型的RegularEnumSet,如果Enum里面的元素个数大于64,则会使用存储大数据量的JumboEnumSet。最后,在createBuilder这个方法里面构建了一个排除引用的建造器excluded,将各式各样的case分门别类地保存起来再返回出去。
最后,我们看到链式调用的最后一步buildAndInstall()。
-
AndroidRefWatcherBuilder#buildAndInstall()
private boolean watchActivities = true; private boolean watchFragments = true; public @NonNull RefWatcher buildAndInstall() { // 1 if (LeakCanaryInternals.installedRefWatcher != null) { throw new UnsupportedOperationException("buildAndInstall() should only be called once."); } // 2 创建RefWatcher对象 RefWatcher refWatcher = build(); if (refWatcher != DISABLED) { // 3 LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true); if (watchActivities) { // 4 监听Activity的引用 ActivityRefWatcher.install(context, refWatcher); } if (watchFragments) { // 5 监听Fragment的引用 FragmentRefWatcher.Helper.install(context, refWatcher); } } // 6 LeakCanaryInternals.installedRefWatcher = refWatcher; return refWatcher; }
首先,在注释1处,会判断LeakCanaryInternals.installedRefWatcher是否已经被赋值,如果被赋值了,则会抛出异常,警告buildAndInstall()这个方法应该仅仅只调用一次,在此方法结束时,即在注释6处,该LeakCanaryInternals.installedRefWatcher才会被赋值。再来看注释2处,调用了AndroidRefWatcherBuilder其基类RefWatcherBuilder的build()方法,我们它是如何建造的。
-
RefWatcherBuilder#build()
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(defa ultReachabilityInspectorClasses()); } return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener, heapDumpBuilder); }
可以看到,RefWatcherBuilder包含了7个组成部分
- 1、excludedRefs : 记录可以被忽略的泄漏路径。
- 2、heapDumpListener : 转储堆信息到hprof文件,并在解析完 hprof 文件后进行回调,最后通知 DisplayLeakService 弹出泄漏提醒。
- 3、debuggerControl : 判断是否处于调试模式,调试模式中不会进行内存泄漏检测。为什么呢?因为在调试过程中可能会保留上一个引用从而导致错误信息上报。
- 4、heapDumper : 堆信息转储者,dump 内存泄漏处的 heap 信息到 hprof 文件
- 5、watchExecutor : 线程控制器,在 onDestroy() 之后并且主线程空闲时执行内存泄漏检测
- 6、gcTrigger : 用于 GC,watchExecutor 首次检测到可能的内存泄漏,会主动进行 GC,GC 之后会再检测一次,仍然泄漏的判定为内存泄漏,最后根据heapDump信息生成相应的泄漏引用链。
- 7、reachabilityInspectorClasses : 用于要进行可达性检测的类列表。
最后,会使用建造者模式将这些组成部分构建成一个新的RefWatcher并将其返回。
我们继续看回到AndroidRefWatcherBuilder的注释3处的 LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true)这行代码。
-
LeakCanaryInternals#setEnabledAsync()
public static void setEnabledAsync(Context context, final Class<?> componentClass, final boolean enabled) { final Context appContext = context.getApplicationContext(); AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() { @Override public void run() { setEnabledBlocking(appContext, componentClass, enabled); } }); }
在这里使用了AsyncTask内部自带的THREAD_POOL_EXECUTOR线程池进行阻塞式地显示DisplayLeakActivity。
然后我们再继续看AndroidRefWatcherBuilder的注释4处的代码。
-
ActivityRefWatcher#install()
public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) { Application application = (Application) context.getApplicationContext(); // 1 ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher); // 2 application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks); }
可以看到,在注释1处创建一个自己的activityRefWatcher实例,并在注释2处调用了application的registerActivityLifecycleCallbacks()方法,这样就能够监听activity对应的生命周期事件了。继续看看activityRefWatcher.lifecycleCallbacks里面的操作。
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks = new ActivityLifecycleCallbacksAdapter() { @Override public void onActivityDestroyed(Activity activity) { refWatcher.watch(activity); } }; public abstract class ActivityLifecycleCallbacksAdapter implements Application.ActivityLifecycleCallbacks { }
很明显,实现并重写了Application的ActivityLifecycleCallbacks的onActivityDestroyed()方法,这样便能在所有Activity执行完onDestroyed()方法之后调用 refWatcher.watch(activity)这行代码进行内存泄漏的检测了。
我们再看到注释5处的FragmentRefWatcher.Helper.install(context, refWatcher)这行代码。
-
FragmentRefWatcher.Helper#install()
public interface FragmentRefWatcher { void watchFragments(Activity activity); final class Helper { private static final String SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME = "com.squareup.leakcanary.internal.SupportFragmentRefWatcher"; public static void install(Context context, RefWatcher refWatcher) { List<FragmentRefWatcher> fragmentRefWatchers = new ArrayList<>(); // 1 if (SDK_INT >= O) { fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher)); } // 2 try { Class<?> fragmentRefWatcherClass = Class.forName(SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME); Constructor<?> constructor = fragmentRefWatcherClass.getDeclaredConstructor(RefWatcher.class); FragmentRefWatcher supportFragmentRefWatcher = (FragmentRefWatcher) constructor.newInstance(refWatcher); fragmentRefWatchers.add(supportFragmentRefWatcher); } catch (Exception ignored) { } if (fragmentRefWatchers.size() == 0) { return; } Helper helper = new Helper(fragmentRefWatchers); // 3 Application application = (Application) context.getApplicationContext(); application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks); } ... }
这里面的逻辑很简单,首先在注释1处将Android标准的Fragment的RefWatcher类,即AndroidOFragmentRefWatcher添加到新创建的fragmentRefWatchers中。在注释2处使用反射将leakcanary-support-fragment包下面的SupportFragmentRefWatcher添加进来,如果你在app的build.gradle下没有添加下面这行引用的话,则会拿不到此类,即LeakCanary只会检测Activity和标准Fragment这两种情况。
debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.2'
继续看到注释3处helper.activityLifecycleCallbacks里面的代码。
private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks = new ActivityLifecycleCallbacksAdapter() { @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { for (FragmentRefWatcher watcher : fragmentRefWatchers) { watcher.watchFragments(activity); } } };
可以看到,在Activity执行完onActivityCreated()方法之后,会调用指定watcher的watchFragments()方法,注意,这里的watcher可能有两种,但不管是哪一种,都会使用当前传入的activity获取到对应的FragmentManager/SupportFragmentManager对象,调用它的registerFragmentLifecycleCallbacks()方法,在对应的onDestroyView()和onDestoryed()方法执行完后,分别使用refWatcher.watch(view)和refWatcher.watch(fragment)进行内存泄漏的检测,代码如下所示。
@Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment fragment) { View view = fragment.getView(); if (view != null) { refWatcher.watch(view); } } @Override public void onFragmentDestroyed(FragmentManagerfm, Fragment fragment) { refWatcher.watch(fragment); }
注意,下面到真正关键的地方了,接下来分析refWatcher.watch()这行代码。
-
RefWatcher#watch()
public void watch(Object watchedReference, String referenceName) { if (this == DISABLED) { return; } checkNotNull(watchedReference, "watchedReference"); checkNotNull(referenceName, "referenceName"); final long watchStartNanoTime = System.nanoTime(); // 1 String key = UUID.randomUUID().toString(); // 2 retainedKeys.add(key); // 3 final KeyedWeakReference reference = new KeyedWeakReference(watchedReference, key, referenceName, queue); // 4 ensureGoneAsync(watchStartNanoTime, reference); }
注意到在注释1处使用随机的UUID保证了每个检测对象对应的
key 的唯一性。在注释2处将生成的key添加到类型为CopyOnWriteArraySet的Set集合中。在注释3处新建了一个自定义的弱引用KeyedWeakReference,看看它内部的实现。 -
KeyedWeakReference
final class KeyedWeakReference extends WeakReference<Object> { public final String key; public final String name; KeyedWeakReference(Object referent, String key, String name, ReferenceQueue<Object> referenceQueue) { // 1 super(checkNotNull(referent, "referent"), checkNotNull(referenceQueue, "referenceQueue")); this.key = checkNotNull(key, "key"); this.name = checkNotNull(name, "name"); } }
可以看到,在KeyedWeakReference内部,使用了key和name标识了一个被检测的WeakReference对象。在注释1处,将弱引用和引用队列 ReferenceQueue 关联起来,如果弱引用referent持有的对象被GC回收,JVM就会把这个弱引用加入到与之关联的引用队列referenceQueue中。即 KeyedWeakReference 持有的 Activity 对象如果被GC回收,该对象就会加入到引用队列 referenceQueue 中。
接着我们回到RefWatcher.watch()里注释4处的ensureGoneAsync()方法。
-
RefWatcher#ensureGoneAsync()
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) { // 1 watchExecutor.execute(new Retryable() { @Override public Retryable.Result run() { // 2 return ensureGone(reference watchStartNanoTime); } }); }
在ensureGoneAsync()方法中,在注释1处使用 watchExecutor 执行了注释2处的 ensureGone 方法,watchExecutor 是 AndroidWatchExecutor 的实例。
下面看看watchExecutor内部的逻辑。
-
AndroidWatchExecutor
public final class AndroidWatchExecutor implements WatchExecutor { ... public AndroidWatchExecutor(long initialDelayMillis) { mainHandler = new Handler(Looper.getMainLooper()); HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME); handlerThread.start(); // 1 backgroundHandler = new Handler(handlerThread.getLooper()); this.initialDelayMillis = initialDelayMillis; maxBackoffFactor = Long.MAX_VALUE / initialDelayMillis; } @Override public void execute(@NonNull Retryable retryable) { // 2 if (Looper.getMainLooper().getThread() == Thread.currentThread()) { waitForIdle(retryable, 0); } else { postWaitForIdle(retryable, 0); } } ... }
在注释1处AndroidWatchExecutor的构造方法中,注意到这里使用HandlerThread的looper新建了一个backgroundHandler,后面会用到。在注释2处,会判断当前线程是否是主线程,如果是,则直接调用waitForIdle()方法,如果不是,则调用postWaitForIdle(),来看看这个方法。
private void postWaitForIdle(final Retryable retryable, final int failedAttempts) { mainHandler.post(new Runnable() { @Override public void run() { waitForIdle(retryable, failedAttempts); } }); }
很清晰,这里使用了在构造方法中用主线程looper构造的mainHandler进行post,那么waitForIdle()最终也会在主线程执行。接着看看waitForIdle()的实现。
private void waitForIdle(final Retryable retryable, final int failedAttempts) { Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() { @Override public boolean queueIdle() { postToBackgroundWithDelay(retryable, failedAttempts); return false; } }); }
这里MessageQueue.IdleHandler()回调方法的作用是当 looper 空闲的时候,会回调 queueIdle 方法,然后执行内部的postToBackgroundWithDelay()方法。接下来看看它的实现。
private void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) { long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor); // 1 long delayMillis = initialDelayMillis * exponentialBackoffFactor; // 2 backgroundHandler.postDelayed(new Runnable() { @Override public void run() { // 3 Retryable.Result result = retryable.run(); // 4 if (result == RETRY) { postWaitForIdle(retryable, failedAttempts + 1); } } }, delayMillis); }
先看到注释4处,可以明白,postToBackgroundWithDelay()是一个递归方法,如果result 一直等于RETRY的话,则会一直执行postWaitForIdle()方法。在回到注释1处,这里initialDelayMillis 的默认值是 5s,因此delayMillis就是5s。在注释2处,使用了在构造方法中用HandlerThread的looper新建的backgroundHandler进行异步延时执行retryable的run()方法。这个run()方法里执行的就是RefWatcher的ensureGoneAsync()方法中注释2处的ensureGone()这行代码,继续看它内部的逻辑。
-
RefWatcher#ensureGone()
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) { long gcStartNanoTime = System.nanoTime(); long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime); // 1 removeWeaklyReachableReferences(); // 2 if (debuggerControl.isDebuggerAttached()) { // The debugger can create false leaks. return RETRY; } // 3 if (gone(reference)) { return DONE; } // 4 gcTrigger.runGc(); removeWeaklyReachableReferences(); // 5 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; }
在注释1处,执行了removeWeaklyReachableReferences()这个方法,接下来分析下它的含义。
private void removeWeaklyReachableReferences() { KeyedWeakReference ref; while ((ref = (KeyedWeakReference) queue.poll()) != null) { retainedKeys.remove(ref.key); } }
这里使用了while循环遍历 ReferenceQueue ,并从 retainedKeys中移除对应的Reference。
再看到注释2处,当Android设备处于debug状态时,会直接返回RETRY进行延时重试检测的操作。在注释3处,我们看看gone(reference)这个方法的逻辑。
private boolean gone(KeyedWeakReference reference) { return !retainedKeys.contains(reference.key); }
这里会判断 retainedKeys 集合中是否还含有 reference,若没有,证明已经被回收了,若含有,可能已经发生内存泄露(或Gc还没有执行回收)。前面的分析中我们知道了 reference 被回收的时候,会被加进 referenceQueue 里面,然后我们会调用removeWeaklyReachableReferences()遍历 referenceQueue 移除掉 retainedKeys 里面的 refrence。
接着我们看到注释4处,执行了gcTrigger的runGc()方法进行垃圾回收,然后使用了removeWeaklyReachableReferences()方法移除已经被回收的引用。这里我们在深入地分析下runGc()的实现。
GcTrigger DEFAULT = new GcTrigger() { @Override public void runGc() { // Code taken from AOSP FinalizationTest: // https://android.googlesource.com/platform/libc ore/+/master/support/src/test/java/libcore/ // java/lang/ref/FinalizationTester.java // System.gc() does not garbage collect every time. Runtime.gc() is // more likely to perform a gc. Runtime.getRuntime().gc(); enqueueReferences(); System.runFinalization(); } private void enqueueReferences() { // Hack. We don't have a programmatic way to wait for the reference queue daemon to move // references to the appropriate queues. try { Thread.sleep(100); } catch (InterruptedException e) { throw new AssertionError(); } } };
这里并没有使用System.gc()方法进行回收,因为system.gc()并不会每次都执行。而是从AOSP中拷贝一段GC回收的代码,从而相比System.gc()更能够保证进行垃圾回收的工作。
最后我们分析下注释5处的代码处理。首先会判断activity 如果还没有被回收,则证明发生内存泄露,进行if判断里面的操作。在里面先调用堆信息转储者heapDumper的dumpHeap()生成相应的 hprof 文件。这里的heapDumper是一个HeapDumper接口,具体的实现是AndroidHeapDumper。我们分析下AndroidHeapDumper的dumpHeap()方法是如何生成hprof文件的。
public File dumpHeap() { File heapDumpFile = leakDirectoryProvider.newHeapDumpFile(); if (heapDumpFile == RETRY_LATER) { return RETRY_LATER; } ... try { Debug.dumpHprofData(heapDumpFile.getAbsolutePath()); ... return heapDumpFile; } catch (Exception e) { ... // Abort heap dump return RETRY_LATER; } }
这里的核心操作就是调用了
Android SDK的API Debug.dumpHprofData() 来生成 hprof 文件。如果这个文件等于RETRY_LATER则表示生成失败,直接返回RETRY进行延时重试检测的操作。如果不等于的话,则表示生成成功,最后会执行heapdumpListener的analyze()对新创建的HeapDump对象进行泄漏分析。由前面对AndroidRefWatcherBuilder的listenerServiceClass()的分析可知,heapdumpListener的实现
就是ServiceHeapDumpListener,接着看到ServiceHeapDumpListener的analyze方法。 -
ServiceHeapDumpListener#analyze()
@Override public void analyze(@NonNull HeapDump heapDump) { checkNotNull(heapDump, "heapDump"); HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass); }
可以看到,这里执行了HeapAnalyzerService的runAnalysis()方法,为了避免减慢app进程或占用内存,这里将HeapAnalyzerService设置在了一个独立的进程中。接着继续分析runAnalysis()方法里面的处理。
public final class HeapAnalyzerService extends ForegroundService implements AnalyzerProgressListener { ... public static void runAnalysis(Context context, HeapDump heapDump, Class<? extends AbstractAnalysisResultService> listenerServiceClass) { ... ContextCompat.startForegroundService(context, intent); } ... @Override protected void onHandleIntentInForeground(@Nullable Intent intent) { ... // 1 HeapAnalyzer heapAnalyzer = new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses); // 2 AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey, heapDump.computeRetainedHeapSize); // 3 AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result); } ... }
这里的HeapAnalyzerService实质是一个类型为IntentService的ForegroundService,执行startForegroundService()之后,会回调onHandleIntentInForeground()方法。注释1处,首先会新建一个HeapAnalyzer对象,顾名思义,它就是根据RefWatcher生成的heap dumps信息来分析被怀疑的泄漏是否是真的。在注释2处,然后会调用它的checkForLeak()方法去使用haha库解析 hprof文件,如下所示:
public @NonNull AnalysisResult checkForLeak(@NonNull File heapDumpFile, @NonNull String referenceKey, boolean computeRetainedSize) { ... try { listener.onProgressUpdate(READING_HEAP_DUMP_FILE); // 1 HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile); // 2 HprofParser parser = new HprofParser(buffer); listener.onProgressUpdate(PARSING_HEAP_DUMP); Snapshot snapshot = parser.parse(); listener.onProgressUpdate(DEDUPLICATING_GC_ROOTS); // 3 deduplicateGcRoots(snapshot); listener.onProgressUpdate(FINDING_LEAKING_REF); // 4 Instance leakingRef = findLeakingReference(referenceKey, snapshot); // 5 if (leakingRef == null) { return noLeak(since(analysisStartNanoTime)); } // 6 return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize); } catch (Throwable e) { return failure(e, since(analysisStartNanoTime)); } }
在注释1处,会新建一个内存映射缓存文件buffer。在注释2处,会使用buffer新建一个HprofParser解析器去解析出对应的引用内存快照文件snapshot。在注释3处,为了减少在Android 6.0版本中重复GCRoots带来的内存压力的影响,使用deduplicateGcRoots()删除了gcRoots中重复的根对象RootObj。在注释4处,调用了findLeakingReference()方法将传入的referenceKey和snapshot对象里面所有类实例的字段值对应的keyCandidate进行比较,如果没有相等的,则表示没有发生内存泄漏,直接调用注释5处的代码返回一个没有泄漏的分析结果AnalysisResult对象。如果找到了相等的,则表示发生了内存泄漏,执行注释6处的代码findLeakTrace()方法返回一个有泄漏分析结果的AnalysisResult对象。
最后,我们来分析下HeapAnalyzerService中注释3处的AbstractAnalysisResultService.sendResultToListener()方法,很明显,这里AbstractAnalysisResultService的实现类就是我们刚开始分析的用于展示泄漏路径信息得DisplayLeakService对象。在里面直接创建一个由PendingIntent构建的泄漏通知用于供用户点击去展示详细的泄漏界面DisplayLeakActivity。核心代码如下所示:
public class DisplayLeakService extends AbstractAnalysisResultService { @Override protected final void onHeapAnalyzed(@NonNull AnalyzedHeap analyzedHeap) { ... boolean resultSaved = false; boolean shouldSaveResult = result.leakFound || result.failure != null; if (shouldSaveResult) { heapDump = renameHeapdump(heapDump); // 1 resultSaved = saveResult(heapDump, result); } if (!shouldSaveResult) { ... showNotification(null, contentTitle, contentText); } else if (resultSaved) { ... // 2 PendingIntent pendingIntent = DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey); ... showNotification(pendingIntent, contentTitle, contentText); } else { onAnalysisResultFailure(getString(R.string.leak_canary_could_not_save_text)); } ... } @Override protected final void onAnalysisResultFailure(String failureMessage) { super.onAnalysisResultFailure(failureMessage); String failureTitle = getString(R.string.leak_canary_result_failure_title); showNotification(null, failureTitle, failureMessage); }
可以看到,只要当分析的堆信息文件保存成功之后,即在注释1处返回的resultSaved为true时,才会执行注释2处的逻辑,即创建一个供用户点击跳转到DisplayLeakActivity的延时通知。
网友评论