2017年5月12日 星期五

Android应用优化之内存概念

12-5-2017 CSDN博客

导语

现在的Android智能手机发展信息万变,从一开始的HTC小米价格战到现在高端市场份额战,在软硬件都发生了翻天覆地的变化。在硬件上内存从一开始的一两百M到现在4G。从软件上我们从一开始为了实现需求而写代码到现在为了代码更健壮、更漂亮而进行不断优化代码。这些都是Android发展的必然一步。今天我来跟大家一起分享Android内存优化的相关概念和实践。

概念

进程内存与RAM之间的关系


进程内存既是虚拟内存(或者叫逻辑内存),而程序的运行需要实实在在的内存,即物理内存(RAM),在需要的时候操作系统会将程序运行中申请的内存(虚拟内存)映射到RAM,让进程能够使用物理内存。

Android中的进程


Google提供的Android整体架构图,可以看到Android系统是基于Linux内核的,但针对移动设备较低的内存和能耗低的需求,Android按照自身需要开发低耗的组件和库,但是Android进程

最明显的内存特征是与zygote共享内存。为了加快启动速度及节约内存,Android应用的进程都是有zygote fork出来的。由于zygote已经载入了完整的Dalvik虚拟机和Android应用框架的代码,fork出的进程和zygote共享同一块内存,这样就节约了每个进程单独载入的时间和内存。

虚拟内存分区


虚拟内存对各种类型的数据进行存储,由于数据的杂乱,因此程序划分五个区域分别管理不同的数据:程序寄存器(Program Count Register)、本地方法栈(Native Stack)、方法区(Methon Area)、栈(Stack)、堆(Heap)

Java虚拟机、Dalvik虚拟机、ART虚拟机的区别


虚拟机(Virtual Machine),这个名词相信大家都不陌生。说到虚拟机我们肯定要说到DalvikJVM

DVM是Dalvik Virtual Machine的缩写,是安卓虚拟机的意思。(为什么不叫AVM->Android Virtual Machine呢?原因是其作者以其祖上居住过的名为Dalvik的村子命名)。

JVM是相对Java Virtual Machine而言的,对于Java(Oracle公司)与Android(Google公司)的关系大家都懂。

JVM运行的是.class字节码,DVM运行的是.dex字节码格式。

Java类被编译成一个或多个字节码.class文件,打包到.jar文件中,java虚拟机从相应的.class文件和.jar文件中获取相应的字节码。

java类被编译成.class文件后,会通过一个dx工具将所有的.class文件转换成一个.dex文件,然后DVM会从其中读取指令和数据。

JVM基于栈,DVM基于寄存器。

JVM基于栈结构,程序在运行时虚拟机需要频繁的从栈上读取写入数据,这个过程需要更多的指令分派与内存访问次数,很耗费CPU时间。

DVM基于寄存器架构,数据的访问通过寄存器间直接传递,这样的访问方式比基于栈方式要快很多。

ART完整名称是Android Runtime。在Android5.0中,ART取代了Dalvik虚拟机(安卓在4.4中发布了ART)。

ART之所以会比Dalvik快,是因为ART执行的是本地机器指令,而Dalvik执行的是Dex字节码,通过通过解释器执行。尽管Dalvik也会对频繁执行的代码进行JIT生成本地机器指令来执行,但毕竟在应用程序运行的过程中将Dex字节码翻译成本地机器机器指令也会影响到应用程序本身的执行,因此即使Dalvik使用了JIT,也在一定程度上也比不上直接就可以执行本地机器指令的运行时。

Android内存分配机制


Android设备出厂以后,虚拟机对单个应用的最大内存分配就确定下来了,如dalvik.vm.heapstartsize=8m,超出这个值就会OOM。而Android为每个进程分配内存的时候,采用了弹性的分配方式,也就是刚开始并不会一下分配很多内存给每个进程,而是给每一个进程分配一个“够用”的量。这个量是根据每一个设备实际的物理内存大小来决定的。随着应用的运行,可能会发现当前的内存可能不够使用了,这时候Android又会为每个进程分配一些额外的内存大小。但是这些额外的大小并不是随意的,也是有限度的,系统不可能为每一个App分配无限大小的内除。对于这个属性值是定义在/system/build.prop文件中,它配置dalvik堆的有关设定。具体设定由如下三个属性来控制:

dalvik.vm.heapstartsize

堆分配的初始大小,调整这个值会影响到应用的流畅性和整体ram消耗。这个值越小,系统ram消耗越慢,但是由于初始值较小,一些较大的应用需要扩张这个堆,从而引发gc和堆调整的策略,会应用反应更慢。相反,这个值越大系统ram消耗越快,但是程序更流畅。dalvik.vm.heapgrowthlimit

受控情况下的极限堆(仅仅针对dalvik堆,不包括native堆)大小,dvm heap是可增长的,但是正常情况下dvm heap的大小是不会超过dalvik.vm.heapgrowthlimit的值。这个值控制那些受控应用的极限堆大小,如果受控的应用dvm heap size超过该值,则将引发oom。dalvik.vm.heapsize

不受控情况下的极限堆大小,这个就是堆的最大值。不管它是不是受控的。这个值会影响非受控应用的dalvikheap size。一旦dalvik heap size超过这个值,直接引发oom

用他们三者之间的关系做一个简单的比喻:分配dalvik heap就好像去食堂打饭,有人饭量大,要吃三碗,有人饭量小,连一碗都吃不完。如果食堂按照三碗的标准来给每个人打饭,那绝对是铺张浪费,所以食堂的策略就是先打一碗,凑合吃,不够了自己再来加,设定堆大小也是一样,先给一个合理值,凑合用,自己不够了再跟系统要。食堂毕竟是做买卖的,如果很多人明显吃不了那么多,硬是一碗接着一碗。为了制止这种不合理的现象,食堂又定了一个策略,一般人就只能吃三碗。但是如果虎背熊腰的大汉确实有需要,可以吃上五碗,超过五碗就不给了(太亏本了)。

沒有留言:

張貼留言