探秘 Android LeakCanary 配置管理模块:原理、源码与实战全解析
一、引言
在 Android 开发的旅程中,内存泄漏始终是一个如影随形的难题,它会让应用程序变得迟缓、不稳定,甚至直接崩溃,极大地影响用户体验。LeakCanary 作为一款强大的内存泄漏检测工具,就像是开发者的“内存侦探”,能够在应用运行时自动检测并报告内存泄漏问题,帮助开发者快速定位并解决隐患。
而配置管理模块则是 LeakCanary 的“大脑”,它允许开发者根据不同的应用场景和需求,对 LeakCanary 的行为进行细致的定制。无论是设置排除某些特定对象的检测,还是调整检测的频率和阈值,都可以通过配置管理模块来实现。本文将深入到 LeakCanary 的源码层面,详细剖析配置管理模块的工作原理,让开发者能够更好地掌握和运用这一强大工具。
二、LeakCanary 配置管理模块概述
2.1 配置管理模块的作用
配置管理模块在 LeakCanary 中承担着至关重要的角色,它负责对 LeakCanary 的各种行为进行参数化配置。这些配置项涵盖了多个方面,包括但不限于内存泄漏检测的阈值、排除特定对象的检测、指定分析结果的处理方式等。通过合理配置这些参数,开发者可以使 LeakCanary 更加贴合应用的实际需求,提高检测的准确性和效率。
2.2 配置管理模块的重要性
不同的 Android 应用具有不同的特点和需求,例如,有些应用对内存的使用非常敏感,需要更严格的内存泄漏检测;而有些应用则可能存在一些特定的对象,由于业务逻辑的原因,它们在表面上看起来像是内存泄漏,但实际上是正常的。配置管理模块允许开发者根据应用的具体情况进行定制化配置,从而避免误报和漏报,提高内存泄漏检测的效果。
2.3 配置管理模块的主要配置项
LeakCanary 的配置管理模块提供了丰富的配置选项,以下是一些常见的配置项:
- 排除引用(Excluded References):指定某些对象或引用不参与内存泄漏检测,避免对正常业务逻辑产生误判。
- 分析结果监听器(Analysis Result Listener):定义当检测到内存泄漏时,LeakCanary 如何处理分析结果,例如显示通知、保存报告等。
- 堆转储触发条件(Heap Dump Trigger Conditions):设置触发堆转储(将应用的内存快照保存到文件中以便后续分析)的条件,如内存使用达到一定阈值或特定对象长时间未被回收。
- 检测频率(Detection Frequency):控制 LeakCanary 进行内存泄漏检测的频率,以平衡检测的准确性和性能开销。
三、配置管理模块的核心类与数据结构
3.1 RefWatcherBuilder 类
RefWatcherBuilder 类是配置管理的入口点,它提供了一系列的方法来设置各种配置参数,并最终构建出一个 RefWatcher 实例,该实例负责实际的内存泄漏检测工作。以下是 RefWatcherBuilder 类的部分源码及详细注释:
// RefWatcherBuilder 类用于构建 RefWatcher 实例,同时设置各种配置参数
public class RefWatcherBuilder<T extends RefWatcherBuilder<T>> {
// 应用的上下文,用于获取系统资源和执行操作
private final Context context;
// 排除的引用集合,存储不需要检测的对象引用
private ExcludedRefs excludedRefs;
// 分析结果的监听器类,用于处理内存泄漏分析结果
private Class<? extends AbstractAnalysisResultService> listenerServiceClass;
// 堆转储文件的目录提供者,用于指定堆转储文件的存储位置
private LeakDirectoryProvider leakDirectoryProvider;
// 检测内存泄漏的延迟时间,单位为毫秒
private long watchDurationMs = TimeUnit.SECONDS.toMillis(5);
// 堆转储的阈值,当内存使用达到该阈值时触发堆转储
private int heapDumpAfterRetainedReferenceCount = 7;
// 堆转储的间隔时间,控制堆转储的频率
private long heapDumpIntervalMs = TimeUnit.MINUTES.toMillis(5);
// 是否在调试器连接时禁用检测
private boolean disableDumpHeap = false;
// 构造函数,初始化应用上下文
public RefWatcherBuilder(Context context) {
this.context = context.getApplicationContext();
}
// 设置排除的引用
public T excludedRefs(ExcludedRefs excludedRefs) {
this.excludedRefs = excludedRefs;
return (T) this;
}
// 设置分析结果的监听器类
public T listenerServiceClass(Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
this.listenerServiceClass = listenerServiceClass;
return (T) this;
}
// 设置堆转储文件的目录提供者
public T leakDirectoryProvider(LeakDirectoryProvider leakDirectoryProvider) {
this.leakDirectoryProvider = leakDirectoryProvider;
return (T) this;
}
// 设置检测内存泄漏的延迟时间
public T watchDuration(long duration, TimeUnit timeUnit) {
this.watchDurationMs = timeUnit.toMillis(duration);
return (T) this;
}
// 设置堆转储的阈值
public T heapDumpAfterRetainedReferenceCount(int heapDumpAfterRetainedReferenceCount) {
this.heapDumpAfterRetainedReferenceCount = heapDumpAfterRetainedReferenceCount;
return (T) this;
}
// 设置堆转储的间隔时间
public T heapDumpInterval(long interval, TimeUnit timeUnit) {
this.heapDumpIntervalMs = timeUnit.toMillis(interval);
return (T) this;
}
// 设置是否在调试器连接时禁用检测
public T disableDumpHeap(boolean disableDumpHeap) {
this.disableDumpHeap = disableDumpHeap;
return (T) this;
}
// 构建并安装 RefWatcher 实例
public RefWatcher buildAndInstall() {
if (isInAnalyzerProcess(context)) {
// 如果当前进程是分析进程,则不进行安装,直接返回禁用的 RefWatcher
return RefWatcher.DISABLED;
}
// 启用显示泄漏信息的 Activity
enableDisplayLeakActivity(context);
// 创建 Android 资源提供者,用于获取字符串资源等
AndroidResourceProvider resourceProvider = new AndroidResourceProvider(context);
// 创建调试器控制实例,用于判断是否有调试器连接
DebuggerControl debuggerControl = new AndroidDebuggerControl();
// 创建堆转储器实例,用于将应用的内存快照保存到文件中
HeapDumper heapDumper = new AndroidHeapDumper(context, resourceProvider);
// 创建资源释放执行器,用于执行资源释放任务
ResourceReleasingExecutor watchExecutor = new AndroidWatchExecutor(Looper.getMainLooper());
// 创建 LeakCanary 内部管理实例,负责管理一些内部状态和操作
LeakCanaryInternals leakCanaryInternals = new LeakCanaryInternals(context);
if (listenerServiceClass != null) {
// 如果设置了分析结果监听器类,则将其设置到 LeakCanary 内部管理实例中
leakCanaryInternals.setListenerServiceClass(listenerServiceClass);
}
// 监听 Activity 的生命周期,当 Activity 销毁时进行内存泄漏检测
leakCanaryInternals.watchActivities(context, watchExecutor);
// 监听 Fragment 的生命周期,当 Fragment 销毁时进行内存泄漏检测
leakCanaryInternals.watchFragments(context, watchExecutor);
// 创建堆转储触发器,根据配置的条件触发堆转储操作
HeapDumpTrigger heapDumpTrigger = new HeapDumpTrigger(context, watchExecutor, debuggerControl, heapDumper,
leakCanaryInternals.leakDirectoryProvider, excludedRefs, watchDurationMs,
heapDumpAfterRetainedReferenceCount, heapDumpIntervalMs, disableDumpHeap);
// 创建 RefWatcher 实例,用于实际的内存泄漏检测
RefWatcher refWatcher = new RefWatcher(watchExecutor, debuggerControl, heapDumpTrigger, heapDumper,
excludedRefs);
// 将 RefWatcher 实例设置为全局的 RefWatcher
LeakCanaryInternals.setRefWatcher(context, refWatcher);
return refWatcher;
}
// 判断当前进程是否是分析进程
private static boolean isInAnalyzerProcess(Context context) {
String processName = LeakCanaryInternals.getProcessName(context);
String analyzerProcessName = context.getPackageName() + ":leakcanary-analyzer";
return analyzerProcessName.equals(processName);
}
// 启用显示泄漏信息的 Activity
private static void enableDisplayLeakActivity(Context context) {
PackageManager packageManager = context.getPackageManager();
ComponentName componentName = new ComponentName(context, DisplayLeakActivity.class);
packageManager.setComponentEnabledSetting(componentName,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
}
}
3.2 ExcludedRefs 类
ExcludedRefs 类用于管理需要排除在内存泄漏检测之外的引用。有时候,应用中可能存在一些对象,它们由于业务逻辑的原因,在表面上看起来像是内存泄漏,但实际上是正常的。通过配置 ExcludedRefs,可以避免 LeakCanary 对这些对象产生误判。以下是 ExcludedRefs 类的源码及注释:
// ExcludedRefs 类用于存储需要排除在内存泄漏检测之外的引用
public final class ExcludedRefs {
// 存储排除的静态字段引用,键为类,值为该类中需要排除的静态字段名集合
private final Map<Class<?>, Set<String>> excludedStaticFields;
// 存储排除的实例字段引用,键为类,值为该类中需要排除的实例字段名集合
private final Map<Class<?>, Set<String>> excludedInstanceFields;
// 私有构造函数,用于初始化排除的静态字段和实例字段集合
private ExcludedRefs(Map<Class<?>, Set<String>> excludedStaticFields,
Map<Class<?>, Set<String>> excludedInstanceFields) {
this.excludedStaticFields = excludedStaticFields;
this.excludedInstanceFields = excludedInstanceFields;
}
// 静态内部类,用于构建 ExcludedRefs 实例
public static final class Builder {
// 用于存储排除的静态字段引用
private final Map<Class<?>, Set<String>> excludedStaticFields = new HashMap<>();
// 用于存储排除的实例字段引用
private final Map<Class<?>, Set<String>> excludedInstanceFields = new HashMap<>();
// 添加需要排除的实例字段引用
public Builder instanceField(Class<?> clazz, String fieldName) {
// 获取该类对应的排除实例字段集合
Set<String> fields = excludedInstanceFields.computeIfAbsent(clazz, k -> new HashSet<>());
// 将字段名添加到集合中
fields.add(fieldName);
return this;
}
// 添加需要排除的静态字段引用
public Builder staticField(Class<?> clazz, String fieldName) {
// 获取该类对应的排除静态字段集合
Set<String> fields = excludedStaticFields.computeIfAbsent(clazz, k -> new HashSet<>());
// 将字段名添加到集合中
fields.add(fieldName);
return this;
}
// 构建 ExcludedRefs 实例
public ExcludedRefs build() {
return new ExcludedRefs(excludedStaticFields, excludedInstanceFields);
}
}
// 获取排除的静态字段引用
public Map<Class<?>, Set<String>> getExcludedStaticFields() {
return excludedStaticFields;
}
// 获取排除的实例字段引用
public Map<Class<?>, Set<String>> getExcludedInstanceFields() {
return excludedInstanceFields;
}
}
3.3 LeakCanaryConfig 类
LeakCanaryConfig 类封装了 LeakCanary 的所有配置信息,它是一个不可变类,确保配置信息在创建后不会被意外修改。以下是 LeakCanaryConfig 类的源码及注释:
// LeakCanaryConfig 类封装了 LeakCanary 的所有配置信息
public final class LeakCanaryConfig {
// 排除的引用集合
public final ExcludedRefs excludedRefs;
// 分析结果的监听器类
public final Class<? extends AbstractAnalysisResultService> listenerServiceClass;
// 堆转储文件的目录提供者
public final LeakDirectoryProvider leakDirectoryProvider;
// 检测内存泄漏的延迟时间,单位为毫秒
public final long watchDurationMs;
// 堆转储的阈值,当内存使用达到该阈值时触发堆转储
public final int heapDumpAfterRetainedReferenceCount;
// 堆转储的间隔时间,控制堆转储的频率
public final long heapDumpIntervalMs;
// 是否在调试器连接时禁用检测
public final boolean disableDumpHeap;
// 私有构造函数,用于初始化配置信息
private LeakCanaryConfig(ExcludedRefs excludedRefs,
Class<? extends AbstractAnalysisResultService> listenerServiceClass,
LeakDirectoryProvider leakDirectoryProvider,
long watchDurationMs,
int heapDumpAfterRetainedReferenceCount,
long heapDumpIntervalMs,
boolean disableDumpHeap) {
this.excludedRefs = excludedRefs;
this.listenerServiceClass = listenerServiceClass;
this.leakDirectoryProvider = leakDirectoryProvider;
this.watchDurationMs = watchDurationMs;
this.heapDumpAfterRetainedReferenceCount = heapDumpAfterRetainedReferenceCount;
this.heapDumpIntervalMs = heapDumpIntervalMs;
this.disableDumpHeap = disableDumpHeap;
}
// 静态内部类,用于构建 LeakCanaryConfig 实例
public static final class Builder {
// 排除的引用集合
private ExcludedRefs excludedRefs;
// 分析结果的监听器类
private Class<? extends AbstractAnalysisResultService> listenerServiceClass;
// 堆转储文件的目录提供者
private LeakDirectoryProvider leakDirectoryProvider;
// 检测内存泄漏的延迟时间,单位为毫秒
private long watchDurationMs = TimeUnit.SECONDS.toMillis(5);
// 堆转储的阈值,当内存使用达到该阈值时触发堆转储
private int heapDumpAfterRetainedReferenceCount = 7;
// 堆转储的间隔时间,控制堆转储的频率
private long heapDumpIntervalMs = TimeUnit.MINUTES.toMillis(5);
// 是否在调试器连接时禁用检测
private boolean disableDumpHeap = false;
// 设置排除的引用集合
public Builder excludedRefs(ExcludedRefs excludedRefs) {
this.excludedRefs = excludedRefs;
return this;
}
// 设置分析结果的监听器类
public Builder listenerServiceClass(Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
this.listenerServiceClass = listenerServiceClass;
return this;
}
// 设置堆转储文件的目录提供者
public Builder leakDirectoryProvider(LeakDirectoryProvider leakDirectoryProvider) {
this.leakDirectoryProvider = leakDirectoryProvider;
return this;
}
// 设置检测内存泄漏的延迟时间
public Builder watchDuration(long duration, TimeUnit timeUnit) {
this.watchDurationMs = timeUnit.toMillis(duration);
return this;
}
// 设置堆转储的阈值
public Builder heapDumpAfterRetainedReferenceCount(int heapDumpAfterRetainedReferenceCount) {
this.heapDumpAfterRetainedReferenceCount = heapDumpAfterRetainedReferenceCount;
return this;
}
// 设置堆转储的间隔时间
public Builder heapDumpInterval(long interval, TimeUnit timeUnit) {
this.heapDumpIntervalMs = timeUnit.toMillis(interval);
return this;
}
// 设置是否在调试器连接时禁用检测
public Builder disableDumpHeap(boolean disableDumpHeap) {
this.disableDumpHeap = disableDumpHeap;
return this;
}
// 构建 LeakCanaryConfig 实例
public LeakCanaryConfig build() {
return new LeakCanaryConfig(excludedRefs, listenerServiceClass, leakDirectoryProvider,
watchDurationMs, heapDumpAfterRetainedReferenceCount, heapDumpIntervalMs, disableDumpHeap);
}
}
}
四、配置管理模块的初始化流程
4.1 初始化入口
LeakCanary 的配置管理模块初始化通常从 LeakCanary.install() 方法开始,该方法会创建一个 RefWatcherBuilder 实例,并进行一系列的默认配置。以下是 LeakCanary.install() 方法的源码及注释:
// LeakCanary 类提供了安装 LeakCanary 的入口方法
public final class LeakCanary {
// 安装 LeakCanary 并返回 RefWatcher 实例
public static RefWatcher install(Application application) {
// 创建 RefWatcherBuilder 实例,传入应用上下文
return refWatcher(application)
.listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
// 创建 RefWatcherBuilder 实例
public static RefWatcherBuilder refWatcher(Context context) {
return new RefWatcherBuilder<>(context);
}
}
4.2 配置参数设置
在 RefWatcherBuilder 中,可以通过链式调用的方式设置各种配置参数。例如:
RefWatcher refWatcher = LeakCanary.refWatcher(context)
.excludedRefs(excludedRefs)
.listenerServiceClass(MyAnalysisResultService.class)
.watchDuration(10, TimeUnit.SECONDS)
.buildAndInstall();
4.3 构建 RefWatcher 实例
在调用 RefWatcherBuilder 的 buildAndInstall() 方法时,会根据配置参数构建 RefWatcher 实例,并将其安装到应用中。以下是 buildAndInstall() 方法的部分关键源码及注释:
public RefWatcher buildAndInstall() {
if (isInAnalyzerProcess(context)) {
// 如果当前进程是分析进程,则不进行安装,直接返回禁用的 RefWatcher
return RefWatcher.DISABLED;
}
// 启用显示泄漏信息的 Activity
enableDisplayLeakActivity(context);
// 创建 Android 资源提供者,用于获取字符串资源等
AndroidResourceProvider resourceProvider = new AndroidResourceProvider(context);
// 创建调试器控制实例,用于判断是否有调试器连接
DebuggerControl debuggerControl = new AndroidDebuggerControl();
// 创建堆转储器实例,用于将应用的内存快照保存到文件中
HeapDumper heapDumper = new AndroidHeapDumper(context, resourceProvider);
// 创建资源释放执行器,用于执行资源释放任务
ResourceReleasingExecutor watchExecutor = new AndroidWatchExecutor(Looper.getMainLooper());
// 创建 LeakCanary 内部管理实例,负责管理一些内部状态和操作
LeakCanaryInternals leakCanaryInternals = new LeakCanaryInternals(context);
if (listenerServiceClass != null) {
// 如果设置了分析结果监听器类,则将其设置到 LeakCanary 内部管理实例中
leakCanaryInternals.setListenerServiceClass(listenerServiceClass);
}
// 监听 Activity 的生命周期,当 Activity 销毁时进行内存泄漏检测
leakCanaryInternals.watchActivities(context, watchExecutor);
// 监听 Fragment 的生命周期,当 Fragment 销毁时进行内存泄漏检测
leakCanaryInternals.watchFragments(context, watchExecutor);
// 创建堆转储触发器,根据配置的条件触发堆转储操作
HeapDumpTrigger heapDumpTrigger = new HeapDumpTrigger(context, watchExecutor, debuggerControl, heapDumper,
leakCanaryInternals.leakDirectoryProvider, excludedRefs, watchDurationMs,
heapDumpAfterRetainedReferenceCount, heapDumpIntervalMs, disableDumpHeap);
// 创建 RefWatcher 实例,用于实际的内存泄漏检测
RefWatcher refWatcher = new RefWatcher(watchExecutor, debuggerControl, heapDumpTrigger, heapDumper,
excludedRefs);
// 将 RefWatcher 实例设置为全局的 RefWatcher
LeakCanaryInternals.setRefWatcher(context, refWatcher);
return refWatcher;
}
五、常见配置项的使用及原理
5.1 排除引用配置
5.1.1 排除引用的作用
在某些情况下,应用中可能存在一些对象或引用,它们由于业务逻辑的原因,在表面上看起来像是内存泄漏,但实际上是正常的。通过排除这些引用,可以避免 LeakCanary 对这些对象产生误判,提高检测的准确性。
5.1.2 配置排除引用的方法
可以使用 ExcludedRefs.Builder 来构建排除引用的集合,并通过 RefWatcherBuilder 的 excludedRefs() 方法进行设置。以下是一个示例:
ExcludedRefs excludedRefs = new ExcludedRefs.Builder()
.instanceField(MyClass.class, "myField")
.staticField(MyOtherClass.class, "staticField")
.build();
RefWatcher refWatcher = LeakCanary.refWatcher(context)
.excludedRefs(excludedRefs)
.buildAndInstall();
5.1.3 排除引用的原理
在内存泄漏检测过程中,LeakCanary 会遍历对象的引用链,当发现某个对象的引用链中包含排除的引用时,会将该对象排除在内存泄漏检测之外。具体实现可以在 HeapAnalyzer 类中找到相关逻辑:
// HeapAnalyzer 类用于分析堆转储文件,检测内存泄漏
public class HeapAnalyzer {
// 排除的引用集合
private final ExcludedRefs excludedRefs;
// 构造函数,初始化排除的引用集合
public HeapAnalyzer(ExcludedRefs excludedRefs) {
this.excludedRefs = excludedRefs;
}
// 分析堆转储文件,检测内存泄漏
public AnalysisResult analyze(HeapDump heapDump) {
// 遍历对象的引用链
for (ObjectInstance objectInstance : heapDump.getInstances()) {
// 检查对象的引用链中是否包含排除的引用
if (isExcluded(objectInstance)) {
// 如果包含排除的引用,则跳过该对象的检测
continue;
}
// 进行内存泄漏检测
// ...
}
// 返回分析结果
return analysisResult;
}
// 检查对象的引用链中是否包含排除的引用
private boolean isExcluded(ObjectInstance objectInstance) {
// 获取对象的类
ClassObj classObj = objectInstance.getClassObj();
// 获取排除的实例字段引用
Set<String> excludedInstanceFields = excludedRefs.getExcludedInstanceFields().get(classObj.getClazz());
if (excludedInstanceFields != null) {
// 检查对象的实例字段是否包含排除的引用
for (FieldInstance fieldInstance : objectInstance.getFields()) {
if (excludedInstanceFields.contains(fieldInstance.getName())) {
return true;
}
}
}
// 获取排除的静态字段引用
Set<String> excludedStaticFields = excludedRefs.getExcludedStaticFields().get(classObj.getClazz());
if (excludedStaticFields != null) {
// 检查对象的静态字段是否包含排除的引用
for (StaticFieldInstance staticFieldInstance : classObj.getStaticFields()) {
if (excludedStaticFields.contains(staticFieldInstance.getName())) {
return true;
}
}
}
return false;
}
}
5.2 分析结果监听器配置
5.2.1 分析结果监听器的作用
当 LeakCanary 检测到内存泄漏并完成分析后,需要将分析结果传递给开发者进行处理。分析结果监听器就是负责接收和处理这些分析结果的组件,开发者可以根据需要自定义监听器的行为,例如显示通知、保存报告等。
5.2.2 配置分析结果监听器的方法
可以通过 RefWatcherBuilder 的 listenerServiceClass() 方法设置分析结果监听器类。以下是一个示例:
RefWatcher refWatcher = LeakCanary.refWatcher(context)
.listenerServiceClass(MyAnalysisResultService.class)
.buildAndInstall();
5.2.3 分析结果监听器的原理
当 LeakCanary 完成内存泄漏分析后,会通过 AbstractAnalysisResultService 的子类(即分析结果监听器类)来处理分析结果。以下是 AbstractAnalysisResultService 类的部分源码及注释:
// AbstractAnalysisResultService 类是分析结果监听器的抽象基类
public abstract class AbstractAnalysisResultService extends IntentService {
// 构造函数,初始化服务名称
protected AbstractAnalysisResultService(String name) {
super(name);
}
// 处理分析结果的方法
protected abstract void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result);
// 处理服务启动的意图
@Override
protected final void onHandleIntent(@Nullable Intent intent) {
if (intent == null) {
CanaryLog.d("AbstractAnalysisResultService received a null intent, ignoring.");
return;
}
// 从意图中获取堆转储文件和分析结果
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAP_DUMP_EXTRA);
AnalysisResult result = (AnalysisResult) intent.getSerializableExtra(RESULT_EXTRA);
if (heapDump == null || result == null) {
CanaryLog.d("AbstractAnalysisResultService received an intent without both heap dump and result, ignoring.");
return;
}
// 调用子类实现的处理方法
onHeapAnalyzed(heapDump, result);
}
// 发送分析结果到监听器
public static void sendResultToListener(Context context,
Class<? extends AbstractAnalysisResultService> listenerServiceClass,
HeapDump heapDump, AnalysisResult result) {
Intent intent = new Intent(context, listenerServiceClass);
intent.putExtra(HEAP_DUMP_EXTRA, heapDump);
intent.putExtra(RESULT_EXTRA, result);
context.startService(intent);
}
}
5.3 堆转储触发条件配置
5.3.1 堆转储触发条件的作用
堆转储是将应用的内存快照保存到文件中,以便后续分析。合理配置堆转储触发条件可以在保证检测准确性的同时,减少不必要的性能开销。
5.3.2 配置堆转储触发条件的方法
可以通过 RefWatcherBuilder 的 heapDumpAfterRetainedReferenceCount() 和 heapDumpIntervalMs 方法设置堆转储的阈值和间隔时间。以下是一个示例:
RefWatcher refWatcher = LeakCanary.refWatcher(context)
.heapDumpAfterRetainedReferenceCount(10)
.heapDumpInterval(2, TimeUnit.MINUTES)
.buildAndInstall();
5.3.3 堆转储触发条件的原理
HeapDumpTrigger 类负责根据配置的触发条件触发堆转储操作。以下是 HeapDumpTrigger 类的部分源码及注释:
// HeapDumpTrigger 类负责根据配置的触发条件触发堆转储操作
public class HeapDumpTrigger {
// 应用上下文
private final Context context;
// 资源释放执行器,用于执行资源释放任务
private final ResourceReleasingExecutor watchExecutor;
// 调试器控制实例,用于判断是否有调试器连接
private final DebuggerControl debuggerControl;
// 堆转储器实例,用于将应用的内存快照保存到文件中
private final HeapDumper heapDumper;
// 堆转储文件的目录提供者
private final LeakDirectoryProvider leakDirectoryProvider;
// 排除的引用集合
private final ExcludedRefs excludedRefs;
// 检测内存泄漏的延迟时间,单位为毫秒
private final long watchDurationMs;
// 堆转储的阈值,当内存使用达到该阈值时触发堆转储
private final int heapDumpAfterRetainedReferenceCount;
// 堆转储的间隔时间,控制堆转储的频率
private final long heapDumpIntervalMs;
// 是否在调试器连接时禁用检测
private final boolean disableDumpHeap;
// 上次堆转储的时间戳
private long lastHeapDumpTime;
// 保留的引用计数
private int retainedReferenceCount;
// 构造函数,初始化各种配置参数
public HeapDumpTrigger(Context context, ResourceReleasingExecutor watchExecutor,
DebuggerControl debuggerControl, HeapDumper heapDumper,
LeakDirectoryProvider leakDirectoryProvider, ExcludedRefs excludedRefs,
long watchDurationMs, int heapDumpAfterRetainedReferenceCount,
long heapDumpIntervalMs, boolean disableDumpHeap) {
this.context = context;
this.watchExecutor = watchExecutor;
this.debuggerControl = debuggerControl;
this.heapDumper = heapDumper;
this.leakDirectoryProvider = leakDirectoryProvider;
this.excludedRefs = excludedRefs;
this.watchDurationMs = watchDurationMs;
this.heapDumpAfterRetainedReferenceCount = heapDumpAfterRetainedReferenceCount;
this.heapDumpIntervalMs = heapDumpIntervalMs;
this.disableDumpHeap = disableDumpHeap;
this.lastHeapDumpTime = 0;
this.retainedReferenceCount = 0;
}
// 监控对象是否泄漏
public void watch(Object watchedReference, String referenceName) {
// 创建一个 KeyedWeakReference 实例,用于弱引用被监控的对象
final KeyedWeakReference reference = new KeyedWeakReference(watchedReference, referenceName,
queue, randomKey());
// 执行任务,检查对象是否泄漏
watchExecutor.execute(() -> checkForLeak(reference, referenceName));
}
// 检查对象是否泄漏
private void checkForLeak(KeyedWeakReference reference, String referenceName) {
// 判断是否有调试器连接,如果有则不进行检测
if (debuggerControl.isDebuggerAttached() && disableDumpHeap) {
return;
}
// 等待一段时间,让垃圾回收器有机会回收对象
waitForGc();
// 判断对象是否已经被回收
if (isGone(reference)) {
CanaryLog.d("%s: GC'd", referenceName);
return;
}
// 增加保留的引用计数
retainedReferenceCount++;
// 判断是否满足堆转储的条件
if (shouldDumpHeap()) {
// 触发堆转储操作
dumpHeap(reference, referenceName);
// 重置保留的引用计数
retainedReferenceCount = 0;
// 更新上次堆转储的时间戳
lastHeapDumpTime = System.currentTimeMillis();
}
}
// 判断是否满足堆转储的条件
private boolean shouldDumpHeap() {
// 判断保留的引用计数是否达到阈值
boolean reachedRetainedCountThreshold = retainedReferenceCount >= heapDumpAfterRetainedReferenceCount;
// 判断堆转储的间隔时间是否满足要求
boolean passedIntervalThreshold = System.currentTimeMillis() - lastHeapDumpTime >= heapDumpIntervalMs;
return reachedRetainedCountThreshold && passedIntervalThreshold;
}
// 触发堆转储操作
private void dumpHeap(KeyedWeakReference reference, String referenceName) {
// 进行堆转储操作,将应用的内存快照保存到文件中
File heapDumpFile = heapDumper.dumpHeap();
if (heapDumpFile == null) {
CanaryLog.d("Could not dump heap.");
return;
}
// 创建 HeapDump 实例,包含堆转储文件和相关信息
HeapDump heapDump = new HeapDump(heapDumpFile, reference.key, referenceName, excludedRefs);
// 发送分析结果到监听器
AbstractAnalysisResultService.sendResultToListener(context, listenerServiceClass, heapDump, null);
}
}
六、配置管理模块的高级用法
6.1 动态配置
在某些情况下,可能需要在应用运行时动态修改 LeakCanary 的配置。可以通过重新构建 RefWatcher 实例来实现动态配置。以下是一个示例:
// 原始配置
RefWatcher refWatcher = LeakCanary.refWatcher(context)
.excludedRefs(excludedRefs)
.listenerServiceClass(MyAnalysisResultService.class)
.buildAndInstall();
// 动态修改配置
ExcludedRefs newExcludedRefs = new ExcludedRefs.Builder()
.instanceField(NewClass.class, "newField")
.build();
RefWatcher newRefWatcher = LeakCanary.refWatcher(context)
.excludedRefs(newExcludedRefs)
.listenerServiceClass(MyAnalysisResultService.class)
.buildAndInstall();
6.2 自定义配置项
如果 LeakCanary 提供的配置项无法满足需求,可以通过继承 RefWatcherBuilder 类来添加自定义配置项。以下是一个示例:
// 自定义 RefWatcherBuilder 类
public class CustomRefWatcherBuilder extends RefWatcherBuilder<CustomRefWatcherBuilder> {
// 自定义配置项
private boolean customOption;
public CustomRefWatcherBuilder(Context context) {
super(context);
}
// 设置自定义配置项
public CustomRefWatcherBuilder customOption(boolean customOption) {
this.customOption = customOption;
return this;
}
@Override
public RefWatcher buildAndInstall() {
// 在构建 RefWatcher 实例之前,可以根据自定义配置项进行一些操作
if (customOption) {
// 执行自定义操作
}
return super.buildAndInstall();
}
}
// 使用自定义 RefWatcherBuilder
RefWatcher refWatcher = new CustomRefWatcherBuilder(context)
.excludedRefs(excludedRefs)
.listenerServiceClass(MyAnalysisResultService.class)
.customOption(true)
.buildAndInstall();
七、配置管理模块的性能优化
7.1 减少不必要的配置
在配置 LeakCanary 时,应尽量减少不必要的排除引用和配置项。过多的排除引用会增加内存泄漏检测的复杂度,降低检测效率。
7.2 合理设置堆转储触发条件
堆转储是一个比较耗时的操作,会对应用的性能产生一定的