Dalvik虚拟机与Java虚拟机有着很多相似的特性,都支持GC,JIT,JNI等等。其主要区别在于文件格式以及指令集不同,下面对两者的特性进行比较与讨论。
Difference1:文件格式
Dalvik的虚拟机类文件采用dex格式,而JVM的类文件是class格式。不同之处在于,我们熟悉的class字节码文件中只包含一个类,而dex文件中可以包含多个类。其好处在于多个类中重复的常数和字符串只会保存一份,一定程度上节省了空间,更适于在手机上运行。
Difference2:指令集
Dalvik的指令集基于寄存器,而JVM的指令集基于堆栈。不同之处在于,基于寄存器的指令在寄存器中寻址,速度更快但指令长度长;基于堆栈的指令集更短,但占用CPU时间较多。其对比类似于CISC与RISC的对比。从移植性而言,显然基于堆栈的指令集可移植性更好,它不针对目标机器的寄存器做任何优化。而基于寄存机的指令集可以再程序运行之前针对目标机器的寄存器对程序进行优化,这种优化是在编译过程中的优化,并非类似于JIT在运行过程中的动态优化,但最终目标都是将程序转化成本地机器语言。
下面,针对Dalvik虚拟机的内存管理,JIT,垃圾收集、JIT、JNI以及进程和线程管理进行讨论。
内存管理
Dalvik虚拟机的内存大体上可以分为三个部分:Java Object Heap、Bitmap Memory和Native Heap。
Java Object Heap用来存放Java对象,不同手机厂商对其大小有不同的设置,可以通过ActivityManager的getMemoryClass获取其最大值,该值是Android应用所能使用的最大内存。
Bitmap Memory用于存放和处理图片。在Android3.0之前,Bitmap Memory是Native Heap的一部分,其大小计入Java Object Heap,即bitmap和其他java对象所占用的内存不能超过上面JOH的最大值。在Android3.0之后,Bitmap Memory直接在JOH中分配,便于GC。
Native Heap是本地机器语言申请分配的内存。
垃圾回收
在Android2.3之前的版本中,Dalvik虚拟机的垃圾回收策略如下:
- 垃圾收集线程在执行的时候,其它的线程都停止,从而一次收集完全部的垃圾,这样导致的结果就是一次垃圾收集造成的程序中止时间通常都大于100ms。
- 在Android2.3之后,垃圾收集线程与其它线程是并发执行,一次可能只收集一部分垃圾,使得一次垃圾收集造成的程序中止时间大大降低,通常都小于5ms。
JIT
JIT是在程序运行的过程中进行编译,将反复调用的代码转化为本地机器码保存起来,这个过程是在运行时动态执行的,相对于程序编译时的优化而言,JIT会占用程序运行时间。Android2.2之后才开始支持JNI。
本地调用
Java程序运行在虚拟机上,需要将程序翻译成目标机器的指令来执行,有些功能需要调用目标机器操作系统接口来完成。Java的本地调用可以帮助我们在Java代码和C\C++代码中互相调用函数。Android提供了NDK来帮助我们实现JNI。
线程管理
Android的进程主要有两个特点:
- 每一个Android APP进程都有一个Dalvik虚拟机实例,这保证了多个app在运行时互不影响。
- 每一个Android应用程序进程都是由一种Zygote的进程fork出来的。而Zygote进程是由初始化进程创造出来的。Zygote进程在启动的时候,会创建一个虚拟机实例,并且在这个虚拟机实例将所有的Java核心库都加载起来。每当Zygote进程需要创建一个Android应用程序进程的时候,它就通过复制自身来实现,也就是通过fork系统调用来实现。这些被fork出来的Android应用程序进程,一方面是复制了Zygote进程中的虚拟机实例,另一方面是与Zygote进程共享了同一套Java核心库。这样不仅Android应用程序进程的创建过程很快,而且由于所有的Android应用程序进程都共享同一套Java核心库而节省了内存空间。