本文来自于腾讯优测公众号(wxutest),未经作者同意,请勿转载,原文地址:http://mp.weixin.qq.com/s/3FTPFvZRqyAQnU047kmWJQ

1.4进阶:内存原理

在上一节里,我们通过深入调查Dalvik虚拟机的方式,解决了Dalvik Heap Pss消耗内存过高的问题。除了Dalvik Heap Pss部分,应用还有其他许多消耗内存的部分。本节里我们就主要介绍其他这些部分的内存是如何被分配和消耗的。

同样以我们的应用为例,在几个版本之后,新加入了一个缓存功能。缓存功能会预先取一些手机的信息,并放在内存中供其他功能使用,这样可以减少后续功能的消耗,加快运行速度。

有了之前的经验,我们自然会想到不能简单粗暴的将所有缓存一次生成,这样可能会产生大量的碎片,因此需要选择一种合适的策略来进行。在选择新功能的缓存策略时,内存测试也同样有用,通过对不同策略的测试,决定那种策略比较有效,并且消耗内存比较少。

在测试过程中我们发现,随着使用不同的策略,Dalvik Heap部分会随之增减。与此同时,不同策略执行代码的时机也会使dalvik other和dex mmap的内存消耗变化。总结规律如下:

  • 不生成缓存时,Dalvik Other和mmap会随之下降。
  • 按需生成缓存时,即使只生成一条记录,Dalvik Other和mmap会增加。
  • 生成多条缓存记录时,Dalvik Other和mmap会在开始增加,然后一直保持不变。
  • Dalvik Other不会下降,mmap偶尔会下降。

通常我们只是大致了解到,Dalvik Other和mmap和代码数量相关,越复杂的应用这部分内存就越多,并没有进行过定量的分析。但现在随着对Dalvik Heap部分的优化,我们发现Dalvik Other和mmap在内存中的比重越来越大。在这个版本里,占总内存的将近一半,不能再置之不理,而是要寻找办法对这部分内存进行优化。

对于这些不熟悉的部分,我们也首先要先去了解背后的原理,才能够针对性的去研究这些内存是如何被消耗的。

1.4.1 从物理内存到应用

我们首先要了解系统的内存机制,搞清楚物理内存是如何被分配到各个进程的,以及共享内存的机制等等。理解内存机制对测试及优化都有很大帮助。

图1-14 Android架构

根据Google提供的Android整体架构图,如图1-14所示,可以看到Android系统是基于Linux内核的,因此底层的内存分配及共享机制与Linux基本相同。但由于Android是为移动设备设计的,所以整套架构为了符合移动设备的特性,需要有较低的内存及能耗需求。因此Android只使用了Linux内核,不使用传统Linux系统的组件。这些组件虽然功能强大,但是较为消耗系统资源。Google开发了若干较小的组件,例如将庞大的glibc换为bionic库,使用sqlite数据库等。Android还扩充了许多内核机制和实现,其中对内存影响较大的是Ashmem和Binder机制。

在Ashmem及COW(Copy-On-Write)机制的基础上,Android进程最明显的内存特征是与zygote共享内存。为了加快启动速度及节约内存,Android应用的进程都是由zygote fork出来的。由于zygote已经载入了完整的Dalvik虚拟机和Android 应用框架的代码,fork出的进程和zygote共享同一块内存这样就节约了每个进程单独载入的时间和内存。应用进程只需要载入自己的dalvik字节码及资源就可以开始工作。

综上所述,一个在运行的Android应用进程会包含以下几个部分:

  • 共享内存:Dalvik虚拟机代码
  • 共享内存:应用框架的代码
  • 共享内存:应用框架的资源
  • 共享内存:应用框架的so库
  • 私有内存:应用的代码
  • 私有内存:应用的资源
  • 私有内存:应用的so库
  • 共享/私有:堆内存,其它部分

有了整体视角后,我们再开始深入,观察某一个应用的内存情况。在之前的测试中,我们使用系统提供的dumpsys meminfo工具来观察内存值。它能够将不同的内存消耗分类统计,输出成便于查看的格式。

但如果我们想细致的研究各部分内存的由来,只靠这个工具是不够的,但我们有必要按照系统划分各部分的方式来理解和分析内存。

通过阅读和分析dumpsys meminfo的代码,我们能够了解到Android是如何划分各部分内存的。下面我们详细讲解dumpsys meminfo工具是如何统计各部分内存值的。

1.4.2 smaps

由于Android底层基于是Linux内核,进程内存信息也和linux一致,所以Dalvik Heap之外的信息都能够从/proc/

在smaps中,列出了进程的各个内存区域,并根据分配的不同用途做标识,以下是root用户使用cat /proc/

dumpsys统计各个内存块的Pss,SharedDirty,PrivateDirty等值,并按以下原则进行了归并:

  • /dev/ashmem/dalvik-heap和/dev/ashmem/dalvik-zygote归为Dalvik Heap。
  • 其它以/dev/ashmem/dalvik-开头的内存区域归为Dalvik Other。
  • Ashmem对应所有/dev/ashmem/下不以dalvik-开头的内存区域。
  • Other dev对应的是以/dev下其他的内存区域。
  • 文件的mmap按已知的几个扩展名分类,其余的归为other mmap。
  • 其它部分,如[stack],[malloc],Unknown等。

了解了dumpsys的方法后,我们可以自己解析smaps,看看归并前各项的内存都是多少。这样能够得到比dumpsys更详细的信息,有助于分析一些问题。

首先将PSS分为以下几大类,计算各部分占比。在这个例子里,几大项是三分天下的节奏。Dalvik和Other dev内存都占了30%以上,剩下的是MMAP和Unknown。进行内存优化时不能只看Dalvik部分,需要同时评估所有的部分。

Dalvik:

Dalvik内存分为多个区域,meminfo统计的是所有区域累加的值。

  • Dalvik_Heap: 包括dalvik-heap和dalvik-zygote

    堆内存,所有的java对象实例都放在这里。
  • LinearAlloc: dalvik-LinearAlloc

    线性分配器,虚拟机存放载入类的函数信息,随着dex里的函数数量而增加。著名的65535个函数的限制就是从这里来的。
  • Accounting: dalvik-aux-structure, dalvik-bitmap, dalvik-card-table

    这部分内存主要做标记和指针表使用。dalvik-aux-structure随着类及方法数目而增大。dalvik-bitmap随着dalvik-heap的 增大而增大。
  • Code_Cache: dalvik-jit-code-cache

    jit编译代码后的缓存,随着代码复杂度的增加变大。

由于堆内存部分往往是应用消耗内存最多的地方,在内存优化中,最常见的方法就是减少Dalvik Heap中创建的对象,能够直接减少Dalvik Heap,并间接减少Accounting部分。减少代码会直接减少运行辅助部分。

在进行不同版本的对比测试时,我们往往会发现Dalvik Other和dex mmap出现了稳定的增长,这是由新加入的代码引入的内存消耗。

根据Dalvik虚拟机的原理,在加载class时,会根据类的变量个数及函数个数申请相应尺寸的内存,作为运行时的内部指针。这部分内存就会体现在LinearAlloc及aux-structure的增长中。随着版本的开发,应用class的数目及复杂度也在不断地增长,因此Dalvik Other部分也在不断地增长。

由于这部分内存的增长取决于代码复杂度,通常情况下并没有简单直接的方法能够降低它们的消耗。但是通过仔细分析他们的组成及原理,还是能够找出一些间接的方法降低这部分内存的,详细方法请见2.6节。

MMAPs:

系统会将一些文件mmap到内存中,对各个文件进行mmap的时机及大小比较复杂。Dex mmap是其中主要的内容。

应用的dex会占据较大的空间,并且随着代码增加使得dex文件变大,占用的内存也会增加。减小dex的(相当于减少代码)尺寸能够降低这部分内存占用,同时也会减少dalvik部分的内存。

1.4.3 zygote共享内存机制

在上一小节,我们介绍了应用各部分内存的含义,读者对dumpsys meminfo输出的大部分数据都能够有所理解。但dumpsys meminfo工具还会输出Heap Size/Alloc/Free部分的数值。我们知道这些数值是Dalvik虚拟机统计的内存堆的使用量,但这些数值是如何对应到Pss内存上的?比如Heap Alloc和Heap Pss往往相差不远,那他们是不是能够看做基本等同的呢?下面我们试图解释这几项数值之间的关系。

由于虚拟机运行时并不区分某个对象实例是Android框架共享的还是应用独有的,Heap Alloc统计的是由虚拟机分配的所有应用实例的内存,所以会将应用从zygote共享的部分也算进去,所以Heap Alloc值总是比实际物理内存使用值要大。

Heap Alloc虽然反映了Java代码分配的内存,但存在框架造成的失真。除此之外,进程还有许多其它部分也需要使用内存。为了准确了解应用消耗的内存,我们要从进程角度而不是虚拟机角度来进行观察。

PSS(Proportional Set Size),表示进程实际使用的物理内存,是由私有内存加上按比例分担计算的各进程共享内存得到的值。例如,如果有三个进程都使用了一个消耗30K内存的so库,那么每个进程在计算这部分PSS值的时候,只会计算10K。总的计算公式是:

Dalvik PSS内存 = 私有内存Private Dirty + (共享内存Shared Dirty / 共享的进程数)

从实际含义来讲,Private Dirty部分存放的是应用new出来的对象实例,是每个应用所独有的,不会再共享。Shared Dirty部分主要是zygote加载的Android框架部分,会被所有Android应用进程共享。通常进程数的值在10-50的范围内。

PSS是一个非常有用的数值,如果系统中所有的进程的PSS相加,所得和即为系统占用内存的总和。但要注意的是,进程的PSS并不代表进程结束后系统能够回收的内存大小。

1.4.4 多进程应用

根据我们在上一节中的描述,当一个进程结束后,它所占用的共享库内存将会被其它仍然使用该共享库的进程所分担,共享库消耗的物理内存并不会减少。实际上,所有共享使用了这个库的应用,PSS内存都会有所增加。对于一般的进程,只是共享着zygote进程的Android框架等基础部分,而通常手机使用时的应用进程数达到几十至上百,所以某个进程结束,其他进程内存增加的情况并不明显。

但对于多进程的应用来说,由于多个进程之间会共享很多内容,包括代码,资源,so库等等,因此单个进程结束造成的影响就会比较明显。以有两个进程的应用为例,进程共享着部分内存,因此当一个进程不再需要这些内存时,就会出现如图1-15所示中的场景。表现为一个进程的内存下降了,另一个进程就会明显的上升。

图1-15两个共享内存进程的内存变化

由此可见,我们在统计多进程的应用内存和进行优化时,需要综合考虑。以免出现努力优化了一个进程的内存,却造成其他进程内存增长的情况。


更多精彩内容欢迎关注腾讯优测的微信公众账号:

腾讯优测是专业的移动云测试平台,为应用、游戏、H5混合应用的研发团队提供产品质量检测与问题解决服务。不仅在线上平台提供app自动化测试、云真机远程操控与调试、私有自动化测试工具XTest等多种质量检测工具,更为VIP客户配备了专家团队提供定制化综合测试解决方案。

【腾讯优测干货分享】如何降低App的待机内存(四)——进阶:内存原理的更多相关文章

  1. 【腾讯优测干货分享】如何降低App的待机内存(二)——规范测试流程及常见问题

    本文来自于腾讯优测公众号(wxutest),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/806TiugiSJvFI7fH6eVA5w 作者:腾讯TMQ专项测 ...

  2. 【腾讯优测干货分享】越用越卡为哪般——如何降低App的待机内存(一)

    本文来自于腾讯优测公众号(wxutest),未经作者同意,请勿转载,原文地址:http://mp.weixin.qq.com/s/1_FKMbi1enpcKMqto-o_FQ 作者:腾讯TMQ专项测试 ...

  3. 【腾讯优测干货分享】如何降低App的待机内存(五)——优化dex相关内存及本章总结

    本文来自于腾讯优测公众号(wxutest),未经作者同意,请勿转载,原文地址:http://mp.weixin.qq.com/s/01Abwe0p1h3WLh28Tzg_Dw 1.5案例:优化dex相 ...

  4. 【腾讯优测干货分享】如何降低App的待机内存(三)——探索内存增长的原因

    本文来自于腾讯优测公众号(wxutest),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/8BiKIt3frq9Yv9KV5FXlGw 1.3新问题的进一步挖 ...

  5. 【腾讯优测干货分享】安卓专项测试之GPU测试探索

    本文来自于Dev Club 开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57c7ffdc0569a1191bce8a63 作者:章未哲——腾讯SNG质 ...

  6. 【腾讯优测干货分享】Android 相机预览方向及其适配探索

    本文来自于腾讯bugly开发者社区,未经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/583ba1df25d735cd2797004d 由于Android系统的开放策略 ...

  7. 【腾讯优测干货分享】微信小程序之自动化亲密接触

    本文来自于腾讯优测公众号(wxutest),未经作者同意,请勿转载,原文地址:http://mp.weixin.qq.com/s/HcPakz5CV1SHnu-U8n85pw 导语 山雨欲来风满楼,最 ...

  8. 【腾讯优测干货分享】Android内存泄漏的简单检查与分析方法

    本文来自于Dev Club 开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57d14047603a5bf1242ad01b 导语 内存泄漏问题大约是An ...

  9. 【腾讯优测干货】看腾讯的技术大牛如何将Crash率从2.2%降至0.2%?

    小优有话说: App Crash就像地雷. 你怕它,想当它不存在.无异于让你的用户去探雷,一旦引爆,用户就没了. 你鼓起勇气去扫雷,它却神龙见首不见尾. 你告诫自己一定开发过程中减少crash,少埋点 ...

随机推荐

  1. StringMVC 中如何做数据校验

    步骤一:引入四个jar包 步骤二:注册类型转换器 <context:component-scan base-package="cn.happy.controller"> ...

  2. 从I/O复用谈epoll为什么高效

    上一篇文章中,谈了一些网络编程的基本概念.在现实使用中,用的最多的就是I/O复用了,无非就是select,poll,epoll 很多人提到网络就说epoll,认为epoll效率是最高的.单纯的这么认为 ...

  3. transtion:过渡动画

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 17.0px Monaco; color: #4f5d66 } p.p2 { margin: 0.0px 0 ...

  4. 07. Web大前端时代之:HTML5+CSS3入门系列~H5 地理位置

    Web大前端时代之:HTML5+CSS3入门系列:http://www.cnblogs.com/dunitian/p/5121725.html 源码:https://github.com/duniti ...

  5. CENTOS 6.5 平台离线安装 Apache2.4

    一.下载Apache 2.4 http://httpd.apache.org/download.cgi http://mirrors.cnnic.cn/apache//httpd/httpd-2.4. ...

  6. MVC Core 网站开发(Ninesky) 1、创建项目

    又要开一个新项目了!说来惭愧,以前的东西每次都没写完,不是不想写完,主要是我每次看到新技术出来我都想尝试一下,看到.Net Core 手又痒了,开始学MVC Core. MVC Core最吸引我的有三 ...

  7. Java 8 的 Nashorn 脚本引擎教程

    本文为了解所有关于 Nashorn JavaScript 引擎易于理解的代码例子. Nashorn JavaScript 引擎是Java SE 8的一部分,它与其它像Google V8 (它是Goog ...

  8. 从零开始编写自己的C#框架(25)——网站部署

    导航 1.关掉访问保护 2.发布网站 3.复制网站到服务器 4.添加新网站 5.设置网站访问权限 6.设置文件夹访问权限 7.控制可更新文件夹执行权限 8.设置“应用程序池”.net版本与模式 9.附 ...

  9. python通过protobuf实现rpc

    由于项目组现在用的rpc是基于google protobuf rpc协议实现的,所以花了点时间了解下protobuf rpc.rpc对于做分布式系统的人来说肯定不陌生,对于rpc不了解的童鞋可以自行g ...

  10. EF6 对多个数据库,多个DBContext的情况 进行迁移的方法。

    参见: http://stackoverflow.com/questions/21537558/multiple-db-contexts-in-the-same-db-and-application- ...