转载

腾讯手机管家实战分析:内存突增是为神马?

应用版本升级后使用内存突增?如何跟踪?这次MIG专项测试组为大家分享内存问题跟踪实战过程!

MIG专项测试组

腾讯手机管家实战分析:内存突增是为神马? 致力于为腾讯移动互联网事业群(MIG)提供专项评测及深度优化(性能、功能、安全等);同时负责探索新的测试理论和方法,研发评测工具及基础组件。

背景

手机管家从4.4升级到4.5后,用户数据反馈待机内存出现了2-4M左右的增长。经过代码排查及MAT分析,发现有几处代码会导致内存增长,只要将这些代码屏蔽掉一部分,内存情况就下降到正常水平。

奇怪的是这些代码在使用过程中分配的内存并不多,只有上百K,甚至有些地方是基本不需要分配内存,但为什么会导致2-4M的内存增长?

初步分析

我们观察了不同版本的meminfo的区别,发现内存的主要增长点是Dalivk部分:

腾讯手机管家实战分析:内存突增是为神马?

从上图可以看到,在Heap Alloc增长273K的情况下,Pss有1M的增长。从HeapFree看出大部分增长的内存是空闲的,而且经过较长时间待机后也没有被释放回系统。

对于Dalvik内存问题,通常先使用MAT辅助分析原因,要关注的有以下几项:

  1. 使用bitmap插件查看是否有多余的图片没有释放
  2. 查找占大块内存较多的对象
  3. 查找其它不需要的对象造成的内存泄漏
  4. 结合代码改动,进行缩减build,统计新代码消耗的内存

经过MAT及缩减编译分析后,基本可以确定是新代码消耗了内存。但却没有发现有明显内存泄漏的地方,而且代码经过review也没有发现问题。

这个结果让我们陷入了困惑,常用的方法找不出问题,说明有更深层次的原因。接下来要从更底层的DVM虚拟机寻找问题。

Dalvik Heap内部是如何分配和释放内存的?

为了弄清楚为什么DVM占着内存不释放,我们阅读了DVM分配内存部分的代码。代码位置在Android源码的dalvik/vm/alloc下,约255K。分析出的主要流程如下。

1、DVM使用mmap/sbrk从系统分配大块内存作为Java Heap。根据系统机制,如果分类的内存尚未真正使用,就不计入PrivateDirty和PSS。

例如下图,Heap Size/Alloc很多,但大部分是共享,实际使用的较少。所以反映到PrivateDirty/PSS里的内存并不多。

腾讯手机管家实战分析:内存突增是为神马?

2、New对象之后,由于要向对应的地址写入数据,内核开始真正分配该地址对应的4K物理内存页面。

Alloc.cpp,176行:

腾讯手机管家实战分析:内存突增是为神马?

3、运行一段时间后,开始GC,有些对象被回收了,有些会一直存在。

腾讯手机管家实战分析:内存突增是为神马?

4、在GC时,有可能会进行trim。即将空闲的物理页面释放回系统,表现为PrivateDirty/PSS下降。

HeapSource.cpp,431行和1304行:

腾讯手机管家实战分析:内存突增是为神马?

释放时是以4K物理页面为单位:

腾讯手机管家实战分析:内存突增是为神马?

问题所在

在了解DVM分配释放内存的机制后,根据meminfo观察到的现象,猜测可能出现了页利用率问题(页内碎片)。如下图所示,

第一行:在开始阶段,内存分配的较满。第二行:经过GC后,大部分对象被释放,少部分留下来。

腾讯手机管家实战分析:内存突增是为神马?

这种情况下可能会产生的问题是,整页的4K内存中可能只有一个小对象,但统计PrivateDirty/PSS时还是按4K计算。

在通常的jvm虚拟机中,有Compacting GC机制,整理内存对象,将散布的内存移动到一起。但根据DVM的代码,DVM的Mark-Sweep算法不能移动对象,即没有内存整理功能,这种情况下就会形成内存空洞。

在猜测了可能的问题后,需要验证是否如猜测原因所致,为此我们需要获取dvm虚拟机的底层内存分配数据,然后按每个物理页面统计所有对象的大小。

在阅读代码的过程中,发现DVM有个内部函数能够遍历所有的内存块,正好能实现我们的需求。

HeapSource.h,161行:

腾讯手机管家实战分析:内存突增是为神马?

接下来的工作就是想办法在native层调用dvmHeapSourceWalk函数,并将我们的回调函数传进去。回调函数记录下来每块内存的地址和大小,保存下来的数据是这样的:

page_start_addr;chunk_start_addr;chunk_end_addr;object_size;chunk_size 41708000;1097891848;1097892056;208;212 41708000;1097892064;1097892320;256;260 41708000;1097892328;1097892488;160;164 41708000;1097892496;1097892656;160;164 41708000;1097892664;1097892968;304;308 41708000;1097892976;1097893136;160;164

再将这些数据按4K页面的范围进行累加,统计每个4K页面的使用率,做出直方图。

腾讯手机管家实战分析:内存突增是为神马?

由此可见,4.5版本相比以前,不满的页面变多了。这就会造成开头说的现象,Heap Free和PSS都增加,很长时间也不会释放。

找到问题代码

为了找到出问题的代码,我们在上一步得到的数据上继续处理。取出所有使用不满2K的页面的内存块地址,再使用OQL将地址导入到MAT中,分析地址对应的对象是什么。

腾讯手机管家实战分析:内存突增是为神马?

对上图得到的对象实例计算dominators。

腾讯手机管家实战分析:内存突增是为神马?

在这里基本就能看出来是哪些对象造成了内存的碎片化。通过对生成这些对象的代码分析和模拟实验,还原出基本的过程:

  1. 生成对象过程需要较多的临时变量
  2. 批量生成过程中,由于还有空闲内存,虚拟机没有做GC
  3. 完成后才进行GC,清除了所有的零时变量,留下碎片化的内存

下图是模拟这个过程的代码,执行这段代码将会在内存中形成很多碎片,造成很高的PSS占用。

腾讯手机管家实战分析:内存突增是为神马?

总结

  1. 最好不要在循环中申请很多内存和创建很多临时变量;
  2. 生成缓存的事,可以慢慢做,也可以按需缓存;
  3. MAT不是万能的,比如这次的数据隐藏在每个对象的地址中;
  4. 了解Linux系统内核对Android测试有帮助;
  5. 内存分配的最小单位是页面,通常为4K;
  6. 对于难缠问题,有必要从底层了解运作机制。

小编有话说

除了在微信公众号为大家定期分享移动质量分析干货外,腾讯Bugly最近开始筹办移动开发者沙龙活动,希望与更多的移动开发者进行面对面交流。

不总结哪来经验,不分享经验何用?

在此小编号召大家多总结,互分享,踊跃给我们投稿,把自己踩过并爬出来的坑树个指示牌警醒后人,让猿们的开发生活更加美好!

投稿方式:将文章和个人介绍邮件到 bugly@tencent.com,字数不限。

本文系腾讯Bugly特邀文章,转载请注明作者和出处“腾讯Bugly(http://bugly.qq.com)”

正文到此结束
Loading...