ResourceCanary 介绍
Matrix 的内存泄漏监控是由 ResourceCanary 实现的,准确的说,ResourceCanary 只能实现 Activity 的内存泄漏检测,但在出现 Activity 内存泄漏时,可以选择 dump 一个堆转储文件,通过该文件,可以分析应用是否存在重复的 Bitmap。
使用
ResourceCanary 是基于 WeakReference 特性和 Square Haha 库开发的 Activity 泄漏和 Bitmap 重复创建检测工具,使用之前,需要进行如下配置:
Matrix.Builder builder = new Matrix.Builder(this);
// 用于在用户点击生成的问题通知时,通过这个 Intent 跳转到指定的 Activity
Intent intent = new Intent();
intent.setClassName(this.getPackageName(), "com.tencent.mm.ui.matrix.ManualDumpActivity");
ResourceConfig resourceConfig = new ResourceConfig.Builder()
.dynamicConfig(new DynamicConfigImplDemo()) // 用于动态获取一些自定义的选项,不同 Plugin 有不同的选项
.setAutoDumpHprofMode(ResourceConfig.DumpMode.AUTO_DUMP) // 自动生成 Hprof 文件
// .setDetectDebuger(true) //matrix test code
.setNotificationContentIntent(intent) // 问题通知
.build();
builder.plugin(new ResourcePlugin(resourceConfig));
// 这个类可用于修复一些内存泄漏问题
ResourcePlugin.activityLeakFixer(this);
复制代码
如果想要在具体的 Activity 中检测内存泄漏,那么获取 Plugin 并执行 start 方法(一般在 onCreate 方法中执行)即可:
Plugin plugin = Matrix.with().getPluginByClass(ResourcePlugin.class);
if (!plugin.isPluginStarted()) {
plugin.start();
}
复制代码
捕获到问题后,会上报信息如下:
{
"tag": "memory",
"type": 0,
"process": "sample.tencent.matrix",
"time": 1590396618440,
"activity": "sample.tencent.matrix.resource.TestLeakActivity",
}
复制代码
如果 DumpMode 为 AUTO_DUMP,还会生成一个压缩文件,里面包含一个堆转储文件和一个 result.info 文件,可以根据 result.info 文件发现具体是哪一个 Activity 泄漏了:
{
"tag":"memory",
"process":"com.tencent.mm",
"resultZipPath":"/storage/emulated/0/Android/data/com.tencent.mm/cache/matrix_resource/dump_result_17400_20170713183615.zip",
"activity":"com.tencent.mm.plugin.setting.ui.setting.SettingsUI",
}
复制代码
配置
ResourcePlugin 执行之前,需要通过 ResourceConfig 配置,配置选项有:
public static final class Builder {
private DumpMode mDefaultDumpHprofMode = DEFAULT_DUMP_HPROF_MODE;
private IDynamicConfig dynamicConfig;
private Intent mContentIntent;
private boolean mDetectDebugger = false;
}
复制代码
其中, ContentIntent 用于发送通知。
DumpMode 用于控制检测到问题后的行为,可选值有:
NO_DUMP,是一个轻量级的模式,会回调 Plugin 的 onDetectIssue 方法,但只报告出现内存泄漏问题的 Activity 的名称
SILENCE_DUMP,和 NO_DUMP 类似,但会回调 IActivityLeakCallback
MANUAL_DUMP,用于生成一个通知,点击后跳转到对应的 Activity,Activity 由 ContentIntent 指定
AUTO_DUMP,用于生成堆转储文件
IDynamicConfig 是一个接口,可用于动态获取一些自定义的选项值:
public interface IDynamicConfig {
String get(String key, String defStr);
int get(String key, int defInt);
long get(String key, long defLong);
boolean get(String key, boolean defBool);
float get(String key, float defFloat);
}
复制代码
和 Resource Canary 相关的选项有:
enum ExptEnum {
//resource
clicfg_matrix_resource_detect_interval_millis, // 后台线程轮询间隔
clicfg_matrix_resource_detect_interval_millis_bg, // 应用不可见时的轮询间隔
clicfg_matrix_resource_max_detect_times, // 重复检测多次后才认为出现了内存泄漏,避免误判
clicfg_matrix_resource_dump_hprof_enable, // 没见代码有用到
}
复制代码
实现该接口对应的方法,即可通过 ResourceConfig 获取上述选项的值:
public final class ResourceConfig {
// 后台线程轮询间隔默认为 1min
private static final long DEFAULT_DETECT_INTERVAL_MILLIS = TimeUnit.MINUTES.toMillis(1);
// 应用不可见时,后台线程轮询间隔默认为 1min
private static final long DEFAULT_DETECT_INTERVAL_MILLIS_BG = TimeUnit.MINUTES.toMillis(20);
// 默认重复检测 10 次后,如果依然能获取到 Activity ,才认为出现了内存泄漏
private static final int DEFAULT_MAX_REDETECT_TIMES = 10;
public long getScanIntervalMillis() { ... }
public long getBgScanIntervalMillis() { ... }
public int getMaxRedetectTimes() { ... }
}
复制代码
可以看到,默认情况下,Resource Canary 在应用可见(onForeground)时每隔 1 分钟检测一次,在应用不可见时每隔 20 分钟检测一次。对于同一个 Activity,在重复检测 10 次后,如果依然能通过弱引用获取,那么就认为出现了内存泄漏。
原理介绍
这部分内容摘抄自官方文档。
监测阶段
在监测阶段,对于 4.0 之前的版本,由于没有 ActivityLifecycleCallbacks,而使用反射有性能问题,使用 BaseActivity 又存在侵入性的问题,因此,ResourceCanary 放弃了对 Android 4.0 之前的版本的支持,直接使用 ActivityLifecycleCallbacks 和弱引用来检测 Activity 的内存泄漏。
分析阶段
在分析阶段,由于对 Activity 的强引用链很可能不止一条,因此问题的关键在于找到最短的引用链。比如有如下引用关系:
那么,将 GC Root 和 Object 1 的引用关系解除即可。对于多条 GC Root 引用链的情况,多次检测即可,这样至少保证了每次执行 ResourceCanary 模块的耗时稳定在一个可预计的范围内,不至于在极端情况下耽误其他流程。
LeakCanary 已实现了上述算法,但 Matrix 改进了其中的一些问题:
增加一个一定能被回收的“哨兵”对象,用来确认系统确实进行了GC
直接通过 WeakReference.get() 来判断对象是否已被回收,避免因延迟导致误判
若发现某个 Activity 无法被回收,再重复判断 3 次(0.6.5 版本的代码默认是 10 次),以防在判断时该 Activity 被局部变量持有导致误判
对已判断为泄漏的 Activity,记录其类名,避免重复提示该 Activity 已泄漏
从 Hprof 文件获取所有冗余的 Bitmap 对象
对于这个问题,Android Moniter 已经有完整的实现,原理简单粗暴:把所有未被回收的 Bitmap 的数据 buffer 取出来,然后先对比所有长度为 1 的 buffer,找出相同的,记录所属的 Bitmap 对象;再对比所有长度为 2 的、长度为 3 的 buffer……直到把所有 buffer 都比对完,这样就记录了所有冗余的 Bitmap 对象,接着再套用 LeakCanary 获取引用链的逻辑把这些 Bitmap 对象到 GC Root 的最短强引用链找出来即可。
性能开销
在监测阶段,Resource Canary 的周期性轮询是在后台线程执行的,默认轮询间隔为 1min,以微信通讯录、朋友圈界面的帧率作为参考,接入后应用的平均帧率下降了 10 帧左右,开销并不明显。但 Dump Hprof 的开销较大,整个 App 会卡死约 5~15s。
分析部分放到了服务器环境中执行。实际使用时分析一个 200M 左右的 Hprof 平均需要 15s 左右的时间。此部分主要消耗在引用链分析上,因为需要广度优先遍历完 Hprof 中记录的全部对象。
关于找一找教程网
本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。
本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。
[Android 性能监控框架 Matrix(1)内存泄漏监控及原理介绍]http://www.zyiz.net/tech/detail-144964.html