Android应用程序是运行在Dalvik虚拟机里面的,并且每一个应用程序对应有一个单独的Dalvik虚拟机实例。 Dalvik虚拟机与Java虚拟机的最显著区别是它们分别具有不同的类文件格式以及指令集。
1 Dalvik虚拟机使用的是dex(Dalvik Executable)格式的类文件,而Java虚拟机使用的是class格式的类文件。
一个dex文件可以包含若干个类,而一个class文件只包括一个类。由于一个dex文件可以包含若干个类,因此它就可以将各个类中重复的字符串和其它常数只保存一次,从而节省了空间,这样就适合在内存和处理器速度有限的手机系统中使用。一般来说,包含有相同类的未压缩dex文件稍小于一个已经压缩的jar文件。
2 Dalvik虚拟机使用的指令是基于寄存器的,而Java虚拟机使用的指令集是基于堆栈的。
基于堆栈的指令很紧凑,例如,Java虚拟机使用的指令只占一个字节,因而称为字节码。基于寄存器的指令由于需要指定源地址和目标地址,因此需要占用更多的指令空间,例如,Dalvik虚拟机的某些指令需要占用两个字节。基于堆栈和基于寄存器的指令集各有优劣,一般而言,执行同样的功能,前者需要更多的指令(主要是load和store指令),而后者需要更多的指令空间。需要更多指令意味着要多占用CPU时间,而需要更多指令空间意味着数据缓冲(d-cache)更易失效。
基于堆栈的指令更具可移植性,因为它不对目标机器的寄存器进行任何假设。然而,基于寄存器的指令由于对目标机器的寄存器进行了假设,因此,它更有利于进行AOT(ahead-of-time)优化。 所谓AOT,就是在解释语言程序运行之前,就先将它编译成本地机器语言程序。AOT本质上是一种静态编译,它是是相对于JIT而言的,也就是说,前者是在程序运行前进行编译,而后者是在程序运行时进行编译。运行时编译意味着可以利用运行时信息来得到比较静态编译更优化的代码,同时也意味不能进行某些高级优化,因为优化过程太耗时了。另一方面,运行前编译由于不占用程序运行时间,因此,它就可以不计时间成本来优化代码。无论AOT,还是JIT,最终的目标都是将解释语言编译为本地机器语言,而本地机器语言都是基于寄存器来执行的,因此,在某种程度来讲,基于寄存器的指令更有利于进行AOT编译以及优化。
Dalvik虚拟机的其它特性:
一. 内存管理
Dalvik虚拟机的内存大体上可以分为Java Object Heap、Bitmap Memory和Native Heap三种。
二. 垃圾收集(GC)
Dalvik虚拟机可以自动回收那些不再使用了的Java Object,也就是那些不再被引用了的Java Object。垃圾自动收集机制将开发者从内存问题中解放出来,极大地提高了开发效率,以及提高了程序的可维护性。
三. 即时编译(JIT)
JIT是相对AOT而言的,即JIT是在程序运行的过程中进行编译的,而AOT是在程序运行前进行编译的。在程序运行的过程中进行编译既有好处,也有坏处。好处在于可以利用程序的运行时信息来对编译出来的代码进行优化,而坏处在于占用程序的运行时间,也就是说不能花太多时间在代码编译和优化之上。
四. Java本地调用(JNI)
无论如何,虚拟机最终都是运行在目标机器之上的,也就是说,它需要将自己的指令翻译成目标机器指令来执行,并且有些功能,需要通过调用目标机器运行的操作系统接口来完成。这样就需要有一个机制,使得函数调用可以从Java层穿越到Native层,也就是C/C++层。这种机制就称为Java本地调用,即JNI。我们在执行Native代码的时候,有时候也是需要调用到Java函数的,这同样是可以通过JNI机制来实现。也就是说,JNI机制既支持在Java函数中调用C/C++函数,也支持在C/C++函数中调用Java函数。
五. 进程和线程管理
一般来说,虚拟机的进程和线程都是与目标机器本地操作系统的进程和线程一一对应的,这样做的好处是可以使本地操作系统来调度进程和线程。进程和线程调度是操作系统的核心模块,它的实现是非常复杂的,特别是考虑到多核的情况,因此,就完全没有必要在虚拟机中提供一个进程和线程库。
关于Android应用程序进程,它有两个很大的特点,下面我们就简要介绍一下。
第一个特点是每一个Android应用程序进程都有一个Dalvik虚拟机实例。这样做的好处是Android应用程序进程之间不会相互影响,也就是说,一个Android应用程序进程的意外中止,不会影响到其它的Android应用程序进程的正常运行。
第二个特点是每一个Android应用程序进程都是由一种称为Zygote的进程fork出来的。Zygote进程是由init进程启动起来的,也就是在系统启动的时候启动的。Zygote进程在启动的时候,会创建一个虚拟机实例,并且在这个虚拟机实例将所有的Java核心库都加载起来。每当Zygote进程需要创建一个Android应用程序进程的时候,它就通过复制自身来实现,也就是通过fork系统调用来实现。这些被fork出来的Android应用程序进程,一方面是复制了Zygote进程中的虚拟机实例,另一方面是与Zygote进程共享了同一套Java核心库。这样不仅Android应用程序进程的创建过程很快,而且由于所有的Android应用程序进程都共享同一套Java核心库而节省了内存空间。
参考文献:
http://blog.csdn.net/luoshengyang/article/details/8852432