Android性能:内存篇之Android虚拟机
在《Android性能:内存篇之虚拟机概论》中我们已经初步了解了JVM的结构基础与内存空间,,但是Android系统中的java虚拟机毕竟不是使用JVM,而是Delvik(Android系统5.0之前版本)与ART(Android4.4及之后版本),在《Android性能:内存篇之内存回收》对内存回收有了深入的了解,接下来我们详细聊聊Android虚拟机的内存回收机制及与虚拟机之间的关联。
一、Dalvik虚拟机
1. Delvik
Dalvik是Google公司自己设计用于Android平台的Java虚拟机,但它运行的不是 .class文件(java字节码),而是.dex文件(dex字节码)。 Dalvik虚拟机包含有一个解释器,用来执行dex字节码,.dex格式是专为Dalvik设计的一种压缩格式,适合内存和处理器速度有限的系统。Dalvik 经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个Dalvik 应用作为一个独立的Linux 进程执行。
主流的大部分Davik采取的都是标注与清理(Mark and Sweep)回收算法,也有实现了拷贝GC算法的(具体算法看《Android性能:内存篇之内存回收》)。
2. 内存分配
Delvik中,内存分配实际上是对堆的分配和释放。当一个 Android 程序启动,应用进程都是从一个叫做 Zygote 的进程衍生出来,系统启动 Zygote 进程后,为了启动一个新的应用程序进程,系统会衍生 Zygote 进程生成一个新的进程,然后在新的进程中加载并运行应用程序的代码。其中,大多数的 RAM pages 被用来分配给Framework 代码,同时促使 RAM 资源能够在应用所有进程之间共享。
为了整个系统的内存控制需要,系统会为每一个应用程序都设置一个堆的限制阈值,整个阈值在不同设备上会因为 RAM 大小不同而有所差异。如果应用占用内存空间已经接近整个阈值时,再尝试分配内存的话,就很容易引起内存溢出的错误。
关于堆的大小,有三个重要数值:Java堆的起始大小(Starting Size)、最大值(Maximum Size)和增长上限值(Growth Limit)。
-dalvik.vm.heapstartsize
-dalvik.vm.heapgrowthlimit
-dalvik.vm.heapsize
- Starting Size : 堆的起始大小,Dalvik虚拟机启动的时候,会先分配一块初始的堆内存给虚拟机使用,这个数值越大,应用启动越流畅,但是系统RAM也消耗越快(一些较大的应用需要扩张这个堆,从而引发GC和堆调整的策略,因此也会使应用反应更慢)。
- Growth Limit:受控情况下的极限堆(仅仅针对dalvik堆,不包括native堆)大小,是系统给每一个程序的最大堆上限,dvm heap是可增长的,但是正常情况下dvm heap的大小是不会超过dalvik.vm.heapgrowthlimit的值,超过这个上限,程序就会OOM。
- Maximum Size:不受控情况下的最大堆内存大小,起始就是我们在用largeheap属性的时候,可以从系统获取的最大堆大小,这个就是堆的最大值。不管它是不是受控的。这个值会影响非受控应用的dalvikheap size。一旦dalvik heap size超过这个值,直接引发oom。
同时除了上面的这个三个指标外,还有几个指标也是值得我们关注的,那就是堆最小空闲值(Min Free)、堆最大空闲值(Max Free)和堆目标利用率(Target Utilization)。
当我们尝试手动去生成一些几百K的对象,试图去扩大可用堆大小的时候,会导致频繁的GC,因为这些对象的分配会导致GC,而GC后会让堆内存回到合适的比例,而我们使用的局部变量很快会被回收理论上存活对象还是那么多,我们的堆大小也会缩减回来无法达到扩充的目的。
3. 对象的分配和GC的步骤

大概步骤是:1. 分配内存;2.内存分配失败,则执行GC(但是不回收Soft的引用);3.分配内存;4.不够?扩大Growth Limit,再次分配内存;5.还不够?指定堆大小到最大,继续GC(释放Soft的引用);6.OOM之前再释放一次,实在不行就OOM吧
二、ART虚拟机
1. ART
ART(Android Runtime)是 Android 上的应用和部分系统服务使用的托管式运行时,4.4开始使用,从Android 5.0(Lollipop)开始,ART就彻底代替了原先的Dalvik,成为Android系统上新的虚拟机,作为运行时的 ART 可执行 Dalvik 可执行文件并遵循 Dex 字节码规范。
Art在GC上不像Dalvik仅有一种回收算法,Art在不同的情况下会选择不同的回收算法,比如Alloc内存不够的时候会采用非并发GC,而在Alloc后发现内存达到一定阀值的时候又会触发并发GC。
2. 内存分配
ART运行时内部使用的Java堆的主要组成包括Image Space、Zygote Space、Allocation Space和Large Object Space四个Space,Image Space用来存在一些预加载的类, Zygote Space和Allocation Space与Dalvik虚拟机垃圾收集机制中的Zygote堆和Active堆的作用是一样的,Large Object Space就是一些离散地址的集合,用来分配一些大对象从而提高了GC的管理效率和整体性能。
3. 对象的分配和GC的步骤
与Delvik基本一致,看上图即可。
三、JVM、Delvik、ART的比较
| JVM | Delvik |
|---|---|
| 基于栈(需要更多指令,移植性好,性能更低) | 基于寄存器(需要更少指令,移植性稍差,性能更佳) |
| 基于不同的硬件提供统一的Java应用运行时环境 | 采用预加载,并不提供统一的环境 |
| 执行的是class文件 | 执行的是dex文件(由class文件转化),占用空间更小 |
| * | 为简化翻译,常量池只使用32位索引 |
| Delvik | ART |
|---|---|
| 安装时是dex字节码,运行需要即时 (JIT1) 编译器翻译成机器码再运行 | 采用了AOT2预编译技术,安装时直接编译成机器码(Android 7.0 中,ART组合使用了AOT和JIT) |
| 占用空间小,启动运行慢 | 占用空间大,安装时间稍长,启动运行更快,节约CPU资源 |
| GC的停顿为2次 | GC的停顿为1次(并发GC) |
| 回收器的总GC时间略长 | 回收器的总GC时间更短 |
| GC次数较少 | 优化了垃圾回收的工效,能够更加及时地进行并行垃圾回收 |
总的来说,ART在GC上做的比Dalvik好太多了,不光是GC的效率,减少pause时间,而且还在内存分配上对大内存的有单独的分配区域,同时还能有算法在后台做内存整理,减少内存碎片。对于开发者来说ART下我们基本可以避免很多类似gc导致的卡顿问题了。另外根据谷歌自己的数据来看,Art相对Dalvik内存分配的效率提高了10倍,GC的效率提高了2-3倍。
本文详细探讨了Android系统中的Dalvik和ART虚拟机,包括两者内存分配、GC机制及其区别。Dalvik运行.dex文件,采用解释器执行,而ART自Android 5.0起成为默认虚拟机,提供了更高效的内存管理和性能。

1708

被折叠的 条评论
为什么被折叠?



