android5.0之前使用的虚拟机是Dalvik虚拟机,这个虚拟机在JVM的基础上做了一定的优化。android5.0之后,android采用了新的虚拟机ART,本文主要对Dalvik、JVM、ART之间进行对比。

##一.Dalvik虚拟机
###1.概念
  Dalvik虚拟机是Android程序的虚拟机,是Android中Java程序的运行基础。其指令集基于寄存器架构,执行其特有的文件格式——dex字节码来完成对象生命周期管理、堆栈管理、线程管理、安全异常管理、垃圾回收等重要功能。它的核心内容是实现库(libdvm.so),大体由C语言实现。依赖于Linux内核的一部分功能——线程机制、内存管理机制,能高效使用内存,并在低速CPU上表现出的高性能。每一个Android应用在底层都会对应一个独立的Dalvik虚拟机实例,其代码在虚拟机的解释下得以执行。
  有一个特殊的虚拟机进程Zygote,他是虚拟机实例的孵化器。它在系统启动的时候就会产生,它会完成虚拟机的初始化,库的加载,预制类库和初始化的操作。如果系统需要一个新的虚拟机实例,它会迅速复制自身,以最快的诉据提供给系统。对于一些只读的系统库,所有虚拟机实例都和Zygote共享一块内存区域。

###2.与JVM对比

android for 虚拟机 android上的虚拟机_Android

  1. Dalvik虚拟机与Java虚拟机的最显著区别是它们分别具有不同的类文件格式以及指令集。Dalvik虚拟机使用的是dex(Dalvik Executable)格式的类文件,而Java虚拟机使用的是class格式的类文件。一个dex文件可以包含若干个类,而一个class文件只包括一个类。由于一个dex文件可以包含若干个类,因此它就可以将各个类中重复的字符串和其它常数只保存一次,从而节省了空间,这样就适合在内存和处理器速度有限的手机系统中使用。一般来说,包含有相同类的未压缩dex文件稍小于一个已经压缩的jar文件。
  2. Dalvik虚拟机使用的指令是基于寄存器的,而Java虚拟机使用的指令集是基于堆栈的。事实上,基于寄存器和基于堆栈的指令集之争,就如精简指令集(RISC)和复杂指令集(CISC)之争,谁优谁劣,至今是没有定论的。
  3. 将多个类文件收集到同一个dex文件中,以便节省空间;
  4. 使用只读的内存映射方式加载dex文件,以便可以多进程共享dex文件,节省程序加载时间;
  5. 提前调整好字节序(byte order)和字对齐(word alignment)方式,使得它们更适合于本地机器,以便提高指令执行速度;
  6. 尽量提前进行字节码验证(bytecode verification),提高程序的加载速度;
  7. 需要重写字节码的优化要提前进行。
  8. 内存管理不同
      Dalvik虚拟机的内存大体上可以分为Java Object Heap、Bitmap Memory和Native Heap三种。
      Java Object Heap是用来分配Java对象的,也就是我们在代码new出来的对象都是位于Java Object Heap上的。Dalvik虚拟机在启动的时候,可以通过-Xms和-Xmx选项来指定Java Object Heap的最小值和最大值。为了避免Dalvik虚拟机在运行的过程中对Java Object Heap的大小进行调整而影响性能,我们可以通过-Xms和-Xmx选项来将它的最小值和最大值设置为相等。
      Java Object Heap的最小和最大默认值为2M和16M,但是手机在出厂时,厂商会根据手机的配置情况来对其进行调整,例如,G1、Droid、Nexus One和Xoom的Java Object Heap的最大值分别为16M、24M、32M 和48M。我们可以通过ActivityManager类的成员函数getMemoryClass来获得Dalvik虚拟机的Java Object Heap的最大值。
      这个Java Object Heap的最大值也就是我们平时所说的Android应用程序进程能够使用的最大内存。这里必须要注意的是,Android应用程序进程能够使用的最大内存指的是能够用来分配Java Object的堆。
      Bitmap Memory也称为External Memory,它是用来处理图像的。在HoneyComb之前,Bitmap Memory是在Native Heap中分配的,但是这部分内存同样计入Java Object Heap中,也就是说,Bitmap占用的内存和Java Object占用的内存加起来不能超过Java Object Heap的最大值。这就是为什么我们在调用BitmapFactory相关的接口来处理大图像时,会抛出一个OutOfMemoryError异常的原因:
      在HoneyComb以及更高的版本中,Bitmap Memory就直接是在Java Object Heap中分配了,这样就可以直接接受GC的管理。
      此外,在HoneyComb以及更高的版本中,我们可以在AndroidManifest.xml的application标签中增加一个值等于“true”的android:largeHeap属性来通知Dalvik虚拟机应用程序需要使用较大的Java Object Heap。事实上,在内存受限的手机上,即使我们将一个应用程序的android:largeHeap属性设置为“true”,也是不能增加它可用的Java Object Heap的大小的,而即便是可以通过这个属性来增大Java Object Heap的大小,一般情况也不应该使用该属性。为了提高系统的整体体验,我们需要做的是致力于降低应用程序的内存需求,而不是增加增加应用程序的Java Object Heap的大小,毕竟系统总共可用的内存是固定的,一个应用程序用得多了,就意味意其它应用程序用得少了。
  9. JNI与NDK
    JNI是java调用C/C++层,JVM和dalvik都有这个能力。不同的在于,为了方便开发者使用C/C++语言来开发应用程序,Android官方提供了NDK。通过NDK,我们就可以使用JNI机制来在Java函数中调用到C/C++函数。不过Android官方是不提倡使用NDK来开发应用程序的,这从它对NDK的支持远远不如SDK的支持就可以看得出来。

##二.ART虚拟机
###1.概念

###2.与Dalvik对比

  1. dalvik是执行的时候编译+运行,安装比较快,开启应用比较慢,应用占用空间小ART是安装的时候就编译好了,执行的时候直接就可以运行的,安装慢,开启应用快,占用空间大用个比喻来说就是,骑自行车dalvik 是已经折叠起来的自行车,每次骑都要先组装自行车才能骑。ART是已经组装好的自行车,每次骑直接上车就能走人。
      要弄清ART和Dalvik的区别 ,首先要清楚apk文件的构成,apk包中除了一堆资源,还有一个重要文件classes.dex,此文件由java字节码优化打包而成,在Dalvik中,每次打开应用的时候,Dalvik会读取这个classes.dex并解释执行(实际情形,还可能先被转换成odex文件,在此忽略);而在ART环境下,当你安装apk的时候,这个classes.dex文件就会被转换成本地机器码——后缀为oat的文件,以后打开应用时直接读取oat文件执行即可。
      举一个不是很恰当的例子:目前国际通行的论文语言都是英文(相当于java字节码),而你作为一个中国人只会中文(相当于arm架构的Android手机),而另一个日本人只会日语(相当于x86架构的Android手机),现在给你一堆英文资料,Dalvik的做法就是将这些资料整理归档,等你需要查找资料时再从书架上找出需要的部分,然后翻译为中文阅读;而ART的做法就是先将所有资料全部翻译为中文单独保存,等你需要时直接找中文资料就可以了。
      所以,ART的优点就是大大提高了执行效率,但是缺点也很明显,一是安装耗时更长,一是占用更多磁盘空间。
  2. ART拥有改进的GC(垃圾回收)机制:GC时更少的暂停时间、GC时并行处理、某些时候Collector所需时间更短、减少内存不足时触发GC的次数、减少后台内存占用。
      首先介绍下dalvik的gc的过程.主要有有四个过程:
    1)当gc被触发时候,其会去查找所有活动的对象,这个时候整个程序与虚拟机内部的所有线程就会挂起,这样目的是在较少的堆栈里找到所引用的对象.需要注意的是这个回收动作是和应用程序同时执行(非并发).
    2)gc对符合条件的对象进行标记
    3)gc对标记的对象进行回收
    4)恢复所有线程的执行现场继续运行
      dalvik这么做的好处是,当pause了之后,gc势必是相当快速的.但是如果出现gc频繁并且内存吃紧势必会导致ui卡顿,掉帧.操作不流畅等.
      后来art改善了这种gc方式(也是想对ui流畅度做贡献,当然关于ui流畅,5.0以上了新的并行ui线程),主要的改善点在将其非并发过程改变成了部分并发.还有就是对内存的重新分配管理
    当art gc发生时:
    1)gc将会锁住java堆,扫描并进行标记
    2)标记完毕释放掉java堆的锁,并且挂起所有线程
    3)gc对标记的对象进行回收
    4)恢复所有线程的执行现场继续运行
    5)重复2-4直到结束
      可以看出整个过程做到了部分并发使得时间缩短.据官方测试数据说gc效率提高2倍。