前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >QAPM新内存分析"专家",帮你分析内存问题根因

QAPM新内存分析"专家",帮你分析内存问题根因

原创
作者头像
硬骨见小鹿
修改2020-12-09 14:42:25
1.4K0
修改2020-12-09 14:42:25
举报
文章被收录于专栏:QAPM-腾讯客户端性能分析

一. 背景

QAPM原有Hprof分析是基于开源项目LeakCanary的shark Andoroid Extension,这里仅有针对安卓内存泄露部分,同时包含了一个极其简陋的内存触顶分析模块,只能根据一定规则获取极少的信息。为了适应更多针对内存触顶的新分析需求:如图片重复,图片超尺寸,字符串重复,对象重复分析与问题引用链聚类等更复杂的Hprof分析,包括获取更多问题信息时,原方案就显得力不从心,因此重构成了唯一的选择。

二. 原生方案的缺陷

原生的内存触顶方案的最致命缺陷在于:能够为技术人员提供的信息太少了,老版本的内存触顶所给出的个例中仅有简单的问题条目以及次数罗列,没有更多的信息出现。因此对技术人员来讲,这样的信息太过于"鸡肋",它只能告诉你发生了什么,却不回答为什么,有多严重,在哪里,以及有什么其他的具体信息。

三. 重构早期原型方案不足

原型方案在早期构建中使用了内存泄漏的shark库,当然也不是一帆风顺的,其中也陆续发现了一些不足之处,具体可以归结为以下几点

  • 原生的堆对象代理体系索引较少,大部分操作使用Lazy Loading甚至为顺序查找,对一些要进行局部统计的操作极端不友好,耗时相对很长。
  • 不保证读取线程安全,多个分析无法在一个索引上同时进行。
  • 使用Okio + Position实现Lazy Loading,需要与IO进行交互,甚至在读取时都要创建一票对象,对GC造成压力,分析速度大大降低。
  • 对象代理体系封装死板,类型系统不够简洁,没有从外部穿透的灵活性,进行复杂业务分析困难,改写也较为困难。

而这些劣势都会引导向两个主要的问题:

  • 更新更复杂的需求难以实现
  • 分析性能低下,体验不佳

四. 重构新方案的目标

因此,我们需要一个全新的Hprof分析组件,以支撑起我们的新需求开发,这个组件的架构主要改进并实现如下特点

  • 大部分对象查找时间复杂度为O(1)
  • 建立索引后必须保证无锁的并发
  • 更简洁,高效,且优雅易用的代理对象体系

五. 新方案思路

1. 底层的改写

在具体实现思路上,我们在索引建立上使用了自己的一套体系,并且拥有全新的对象代理。

与shark不同,我们采用了较为激进的Eager Loading,对分析中常见的操作都建立了索引表,保证分析器查找取用数据的速度。这样一来也可以保证在索引建立完成后,所有的读取都是线程安全的,我们可以尽情的利用多核处理器的能力。

在另外一个层面上,根据业务的实际需求,我们针对代理对象的最短引用链获取做了特别的处理。即在分析时就将最短引用链求出,而不必像原有shark那样在用到时再进行计算。且在实际业务中要获取谁的引用链是无法预知的,这就造成了一个碰运气的问题:如果对象在BFS中遍历处于靠后的位置,或者是其根本从gc root不可达,再加之老方案遍历时是通过访问字段,而字段的加载又是极大可能要触发IO的。这样一整套组合拳下来,整个分析体验就会变得尤其糟糕。新Hprof通过牺牲一些内存,换取高速的引用链获取,极大地提高了体验。

改动前:

老框架
老框架

改动后:

新框架.jpg
新框架.jpg

2. 分析器的框架

整个分析器通过注解,反射,DI等技术实现动态控制具体分析器在某次分析中的参与,以及不同分析器的参数设置,精确控制分析器的行为。这也使得后续的维护与扩展工作趋于轻量化,对运维同学友好了许多。

六. 实现过程

在上面的方向指引下我们已经完成了初版以及后续的改良版本,效果良好。

而针对新索引的发掘与探索也是在初版改进了几次后才逐渐萌生重写想法并且最终成型的。在这其中我们经历了如下的历程:

初步原型构建

这个阶段我们直接使用了shark的体系,对控制层与应用层各个分析器进行了开发,满足了基本的分析需求。在初步的版本中,由于没有引用链分析的加入以及使用的hprof较为简单,除开启动预热时间,我们没有发现特别突出的性能问题。然而在加入了引用链分析后,甚至在简单hprof中多个分析器并行获取引用链也会消耗大量的时间与内存。

发现问题

在上一阶段中,我们发现由于引用链的并行获取,造成了时间的大量消耗与内存的飙高。问题在哪呢?

经过分析,我们得出一个结论,由于当时仍然是处于shark的体系之下,其线程不安全的读取让整个支持并行的策略看起来既滑稽又无奈:为多个分析器分配多个hprof对象,并且分别并行构建。这也直接导致了我们很难在进行分析前就将统一的最短引用链求出,当然其代码封装的高度不灵活性也是阻力的来源。

不仅如此,在原有体系下针对对象的全盘统计也是极为痛苦的,通过Profiling我们发现大多数时间都被耗费在了Okio与磁盘的读取交互上,让人无法接受。

诸多不满之下,更换到一个新的索引系统的想法诞生了。

解决问题

在设计全新索引和代理体系时,我们尽可能将常用的查询通过映射缓存起来,例如类型名到代理类型对象等,此等操作在原有的索引下是实打实的O(n)时间消耗。这使得我们的任何统计操作时间被大大缩短。

并且针对到以后可能出现的复杂分析,我们特地为对象缓存了一个可达表与对应的可达性类型(实例字段,静态字段,JNI Local等)。

同时我们也借助上面的可达表进行对象最短引用链的构建,以一定的内存牺牲来使得引用链获取是无需任何时间的。

初期的测试中,我们都是使用的较为简单的hprof文件,在新索引体系下没有任何内存与时间消耗问题,但在对真实业务hprof分析时发现了高内存占用与高时间消耗。

全新的索引系统为我们带来的优点与缺点

优点

  • 绝大多数的 O(1) 查询
  • 统一获取的最短引用链
  • 便于复杂分析的可达性表

缺点

  • 代理对象字段的错误设计
  • 相较之下较高的内存占用

为了解决面对实际业务中的hprof建立索引后高内存占用问题,我们做了与内存相关的性能分析,最终确认了是错误的代理体系设计导致了对象的增多,GC压力也随之增加。

意识到这是一个代理对象系统设计的失误时,我们及时调整了实现方式,放弃一个字段分配一个代理对象的方法,改为使用方法指定加载且完全线程安全的按需加载,并且最终实现了时间消耗与内存占用的平衡。

七. 阶段性成果

新分析器的加入

在原有的泄露基础上,我们加入了四个对内存优化具有针对性的分析器:

  • 字符串重复
  • Bitmap重复
  • Bitmap超尺寸探测
  • 普通对象重复

普适性的引用链聚类分析加入

除开泄露分析器,其他分析器也充分利用上了预加载的最短引用链信息,通过在一组内分析引用链的相似段,找出最普遍的引用链特征,精准定位群体事件的问题所在。

八. 新的专家为你带来了什么

  1. 更详细的信息:GC引用链,图片的预览,尺寸,像素通道,字符串的内容等等
  2. 配合提单系统的修复闭环

在更多的信息和高效的修复闭环系统下可以大大提高开发人员的效率,避免了开发人员通过各类工具进行人工操作和分析,快速定位问题所在,将更多精力投放在修复问题上。这也进一步减少了人力消耗,让团队效率更进一步。

老页面

列表

列表
列表

详细信息

详情
详情

详细信息

详情
详情

新页面

列表

列表
列表

详细的个例信息,以及附加的数据

详情
详情

下半部GC引用链

详细信息
详细信息

如果可用的图片预览:可放大查看

提单内容

旧内存分析 vs. 新内存分析 vs. LeakCanary 2

新内存分析

旧内存分析

LeakCanary 2

分析项

多样化,根据分析器制订

少量分析,根据规则制定

仅有泄露

分析结果

针对不同类型有专门化的详细信息(如图片的尺寸,像素信息等)

便利功能

拥有图片预览导出,字符串内容预览等便捷功能

少量

九. 后续规划

虽然目前已经取得了一些成果,但这还远远不够。

  1. 我们需要更多的分析器加入,如对于普通集合类型的低效利用(过短或者持有过多的空引用),引用值类型的分析(java.lang.Integer)等。
  2. 导出更多分析信息(例如针对Bitmap在不同Android版本的信息获取),来更好的定位内存中的问题所在。
  3. 美化信息的输出,提供更加易读,准确的结果。
  4. 考虑提供演进更优的框架设计,获取更好的性能来提升我们的分析体验。
  5. 考虑使用更适合模拟大量小对象的语言进行重写

如有兴趣或任何疑问,请联系在线客服:QAPM

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一. 背景
  • 二. 原生方案的缺陷
  • 三. 重构早期原型方案不足
  • 四. 重构新方案的目标
  • 五. 新方案思路
    • 1. 底层的改写
      • 2. 分析器的框架
      • 六. 实现过程
        • 初步原型构建
          • 发现问题
            • 解决问题
              • 全新的索引系统为我们带来的优点与缺点
          • 七. 阶段性成果
            • 新分析器的加入
              • 普适性的引用链聚类分析加入
              • 八. 新的专家为你带来了什么
                • 老页面
                  • 新页面
                    • 旧内存分析 vs. 新内存分析 vs. LeakCanary 2
                    • 九. 后续规划
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档