🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] ## Memory Profiler `Memory Profiler`是`Profiler` 中的其中一个版块,`Profiler` 是 `Android Studio` 为我们提供的性能分析工具,使用 `Profiler` 能分析应用的 `CPU`、内存、网络以及电量的使用情况。 使用`Memory`可以检测以下功能 1.查看内存曲线及内存占用情况 2.可以定位是否存在内存抖动问题 3.堆转储(`Dump Java Heap`)可检测出内存泄漏的对象 ![](https://img.kancloud.cn/c8/65/c8652b7c610b836fa9895571f3281e55_1240x761.png) 关于`Memory Profiler`的具体使用就不在此缀述了,想要了解的可参考:[什么是 Memory Profiler?](https://juejin.cn/post/6844903897958449166#heading-48 "https://juejin.cn/post/6844903897958449166#heading-48") * **Java**:从 Java 或 Kotlin 代码分配的对象内存。 * **Native**:从 C 或 C++ 代码分配的对象内存。 即使您的应用中不使用 C++,您也可能会看到此处使用的一些原生内存,因为 Android 框架使用原生内存代表您处理各种任务,如处理图像资源和其他图形时,即使您编写的代码采用 Java 或 Kotlin 语言。 * **Graphics**:图形缓冲区队列向屏幕显示像素(包括 GL 表面、GL 纹理等等)所使用的内存。 (请注意,这是与 CPU 共享的内存,不是 GPU 专用内存。) * **Stack**: 您的应用中的原生堆栈和 Java 堆栈使用的内存。 这通常与您的应用运行多少线程有关。 * **Code**:您的应用用于处理代码和资源(如 dex 字节码、已优化或已编译的 dex 码、.so 库和字体)的内存。 * **Other**:您的应用使用的系统不确定如何分类的内存。 ## showmap 这对于我们来说非常有用,可以确定进程中哪些库占用内存比较多 ~~~ dumpsys meminfo com.sangfor.pocket ps | grep com.sangfor.pocket su showmap [pid] ~~~ ## Memory Analyzer Tool `MAT`工具可以帮助开发者定位导致内存泄漏的对象,以及发现大的内存对象,然后解决内存泄漏并通过优化内存对象,以达到减少内存消耗的目的。 比起`Memory Profiler`,`MAT`使用起来更加麻烦,同时现在`Memory Profiler`功能也越来越强大了,所以我现在已经很少使用`MAT`了 如果想要更多地了解`MAT`,也可以参考:[什么是Memory Analyzer Tool](https://juejin.cn/post/6844903897958449166#heading-52 "https://juejin.cn/post/6844903897958449166#heading-52") ## LeakCanary eakCanary是一个内存泄漏检测的框架,默认只会检测Activity的泄漏,如果需要检测其他类,可以使用LeakCanary.install返回的RefWatcher,调用RefWatcher.watch(obj)就可以观测obj对象是否出现泄漏。 ### 基本原理 LeakCanary 采用的是 Reference + ReferenceQueue来检测一个对象是否被回收,大概需要几个步骤: * 创建一个引用队列 queue * 创建 Refrence 对象,并关联引用队列 queue * 在 reference 被回收的时候,refrence 会被添加到 queue 中 ### 基本流程 ![](https://img.kancloud.cn/1d/37/1d3759080e64e45409577643ce1505d5_862x675.png) #### 检测泄露 观察对象:`Activity`,`Fragment`,`RootView`,`Service 1. 传入的观察对象都会被存储在`watchedObjects`中 2. 会为每个`watchedObject`生成一个`KeyedWeakReference`弱引用对象并与一个`queue`关联,当对象被回收时,该弱引用对象将进入`queue`当中 3. 在检测过程中,我们会调用多次`removeWeaklyReachableObjects`,将已回收对象从`watchedObjects`中移除 4. 如果`watchedObjects`中没有移除对象,证明它没有被回收,那么就会调用`moveToRetained` #### 触发堆快照,生成`hprof`文件 1. 如果`retainedObjectCount`数量大于0,则进行一次`GC`,避免额外的`Dump` 2. 默认情况下,如果`retainedReferenceCount<5`,不会进行`Dump`,节省资源 3. 如果两次`Dump`之间时间少于60s,也会直接返回,避免频繁`Dump` 4. 调用`heapDumper.dumpHeap()`进行真正的`Dump`操作 5. `Dump`之后,要删除已经处理过了的引用 6. 调用`HeapAnalyzerService.runAnalysis`对结果进行分析 分析`hprof`文件的工作主要是在`HeapAnalyzerService`类中完成的 关于`Hprof`文件的解析细节,就需要牵扯到`Hprof`二进制文件协议,通过阅读协议文档,`hprof`的二进制文件结构大概如下: ![](https://img.kancloud.cn/7f/9e/7f9e33fbe3cad99283d8ee58bd184cdf_968x220.png) 解析流程如下所示: ![](https://img.kancloud.cn/36/08/3608ff9adc07a3071e8cb7b010863a68_193x586.png) 简要说下流程: 1.解析文件头信息,得到解析开始位置 2.根据头信息创建`Hprof`文件对象 3.构建内存索引 4.使用`hprof`对象和索引构建`Graph`对象 5.查找可能泄漏的对象与`GCRoot`间的引用链来判断是否存在泄漏(使用广度优先算法在`Graph`中查找) ### 为什么`LeakCanary`不能用于线上? 1. 每次内存泄漏以后,都会生成一个`.hprof`文件,然后解析,并将结果写入`.hprof.result`。增加手机负担,引起手机卡顿等问题。 2. 多次调用`GC`,可能会对线上性能产生影响 3. 同样的泄漏问题,会重复生成 `.hprof` 文件,重复分析并写入磁盘。 4.`. hprof`文件较大,信息回捞成问题。 了解了这些问题,我们可以尝试提出一些解决方案: 1. 可以根据手机信息来设定一个内存阈值 `M` ,当已使用内存小于 `M` 时,如果此时有内存泄漏,只将泄漏对象的信息放入内存当中保存,不生成`.hprof`文件。当已使用大于 `M` 时,生成`.hprof`文件 2. 当引用链路相同时,可根据实际情况去重。 3. 不直接回捞`.hprof`文件,可以选择回捞分析的结果 4. 可以尝试将已泄漏对象存储在数据库中,一个用户同一个泄漏只检测一次,减少对用户的影响 ## ResourceCanary ### LeakCanary的缺陷 LeakCanary分析可见其对 Activity 是否泄漏的判断依赖VM会将可回收的对象加入 WeakReference 关联的 ReferenceQueue 这一特性,Matrix发现这中做法在个别系统上可能存在误报,原因大致如下: * VM 并没有提供强制触发 GC 的 API ,通过 System.gc()或 Runtime.getRuntime().gc()只能“建议”系统进行 GC ,如果系统忽略了我们的 GC 请求,可回收的对象就不会被加入 ReferenceQueue * 将可回收对象加入 ReferenceQueue 需要等待一段时间,LeakCanary 采用延时 100ms 的做法加以规避,但似乎并不绝对管用 * 监测逻辑是异步的,如果判断 Activity 是否可回收时某个 Activity 正好还被某个方法的局部变量持有,就会引起误判 * 若反复进入泄漏的 Activity ,LeakCanary 会重复提示该 Activity 已泄漏 ### ResourceCanary的改进 对此做了以下改进: * 增加一个一定能被回收的“哨兵”对象,用来确认系统确实进行了GC * 直接通过WeakReference.get()来判断对象是否已被回收,避免因延迟导致误判 * 若发现某个Activity无法被回收,再重复判断3次,且要求从该Activity被记录起有2个以上的Activity被创建才认为是泄漏,以防在判断时该Activity被局部变量持有导致误判 * 对已判断为泄漏的Activity,记录其类名,避免重复提示该Activity已泄漏 ### 修复系统潜在内存泄漏 Matrix ResourceCanary 还针对常见的系统内存泄漏做了针对性的修改。 [InputMethodManager 导致的泄漏](https://www.jianshu.com/p/62e498075e56) [Drawable泄露](https://www.jianshu.com/p/914888735ce5) [【带着问题学】关于LeakCanary2.0你应该知道的知识点](https://juejin.cn/post/6968084138125590541) [探索 Android 内存优化方法](https://juejin.cn/post/6844903897958449166)