Dalvik和ART
这是我第三次写Dalvik(以下简称DVM)和ART虚拟机了,它们都是Android手机上运行java代码的虚拟虚拟机。DVM不是JVM,主要还是因为DVM的实现没有遵守JVM的实现规范。
DVM与JVM基于的架构不同
JVM是基于栈的,当它需要到栈中去读写数据时,所需的指令就因此而增多,将导致速度变慢。手机的使用要求就是要快,显然JVM是不能满足这一性能要求。所以google就用Dalvik来解决这个性能问题。DVM是基于寄存器,因此它不需要像JVM那样在复制数据时要使用大量的出入栈指令。不过因为DVM由于显式指定了操作数,因此DVM基于寄存器的指令会比基于栈的指令要大,同时也因此减少了指令的数量。
DVM与JVM执行字节码的方式不同
在Java SE程序中,Java类被编译成一个或多个.class文件,并打包成.jar文件,程序运行时,JVM会通过相应的.class文件和jar文件获取相应的字节码。它的顺序是.java文件->.class文件->.jar文件。
.jar文件里面包含多个.class文件,每个.class文件里面包含了该类的常量池、类信息、属性等,当JVM加载该.jar文件时,会加载里面所有的.class文件,JVM这种加载方式很慢,对于内存有限制的移动设备来说非常不合适的。
而DVM则是用dx工具将所有的.class文件转换为一个.dex文件,当应用程序运行时,DVM会从该.dex文件读取指令和数据。它的顺序是.java文件->.class文件->.dex文件。apk文件里面只包含了一个.dex文件,这个.dex文件将所有的.class文件里面所包含的信息全部都整合在一起了。这样加载速度会快很多。.dex文件由类加载器处理,接着解释器根据指令集对Dalvik字节码进行解释、执行,最后交给Linux处理。另外,.class文件会有很多冗余信息,dex工具会去除冗余信息,并把所有.class文件整合到.dex文件中,可以减少I/O操作,加快了类的查找速度。
一个应用程序对应一个DVM实例
在Android中的每一个应用都运行在一个DVM实例中,而每一个DVM实例都运行在一个独立的进程空间中,独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。
DVM是由Zygote创建和初始化
Zygote进程本身也是一个DVM进程。它同时也是用来创建和初始化DVM实例的。每当系统需要创建一个应用程序时,Zygote进程就fork自身(其就是复制自己,用了写时复制),快速地创建和初始化一个DVM实例,用于应用程序的运行。对于一些只读的系统库,所有的DVM实例都会和Zygote共享一块内存区域,从而节省了内存开销。
DVM与JIT(即时编译)
DVM还没有加入JIT功能时,DVM每次执行代码,都需要通过解释器将.dex编译成机器码,然后交给系统处理,但效率不高。为了提高效率,在Android2.2后就加入了JIT,它会对多次运行的代码(热点代码)进行编译,生成相当精简的本地机器码,这样下次执行到相同逻辑时,就可以直接使用编译之后的本地机器码,而不是每次都需要编译。不过,需要注意的是:应用程序每一次重新运行的时候,都需要重新做这个编译工作 ,因此每次重新打开应用程序,都需要JIT编译。
DVM的运行时堆
DVM的运行堆使用标记-清除算法进行GC,由两个Space和多个辅助数据结构组成。两个Space分别是Zygote Space(Zygote Heap)和Allocation Space(Active Heap)。Zygote Space用来管理Zygote进程在启动过程中预加载和创建的各种对象,它不会触发GC。在Zygote进程和应用程序之间会共享Zygote Space。在Zygote进程fork第一个子进程之前,会把Zygote Space分成两个部分,原来的已经被使用的那部分仍旧叫Zygote Space,而未使用那一个部分则就叫Allocation Space,以后的对象都会在Allocation Space上进行分配和释放。Allcation Space不是进程间共享的,在每个进程中都独立拥有一份。除了这两个Space,还有以下的辅助数据结构:
Card Table:当第一个次进程垃圾标记后,记录垃圾信息
Heap Bitmap:有两个Heap Bitmap,一个用来记录上次GC存活的对象 ,另一个用来记录这次GC存活的对象。
Mark Stack:在GC标记的阶段使用,它用来遍历存活象。
ART的时代
在Android4.4时,发布了ART虚拟机,用来替代Dalvik虚拟机,但anroid4.4上默认还是运行Dalvik。Android 5.0后才默认使用ART虚拟机。
ART与DVM的区别
DVM是为32位CPU设计的(注定会被淘汰),而ART支持64位并且兼容32位CPU
ART将GC暂停由2次改为1次
ART的运行时堆与DVM不同
DVM中的应用每次运行时,字节码都需要通过JIT编译器编译为机器码,这会使用应用程序的运行效率降低,在ART中,系统在安装应用时会进行一次AOT(ahead of time compilation,预编译),将字节码预先编译成机器码并存储在本地,这样应用程序运行时就不需要执行编译了,运行效率会大大提升,设备的耗电也会降低。采用AOT也会有缺点,主要有两个:(1)AOT会使得应用程序的安装时间变长,尤其是一些复杂的应用;(2)字节码预先编译成机器码,机器码需要的存储空间会多一些。为了解决这两个问题,在Android7.0中的ART加入了JIT,作为AOT的一个补充,在应用程序安装时并不会将字节码全部编译成机器码,而是在运行中将热点代码编译成机器码,从而缩短应用程序安装时间并节省了存储空间。
ART的运行时堆
与DVM的GC不同,ART采用了多种垃圾收集方案,每个方案会运行不同的垃圾收集器,默认是采用了CMS(Concurrent Mark-Sweep)方案,该方案主要使用了sticky-CMS和partial-CMS。根据不同的CMS方案,ART的运行时堆的空间也会有不同的划分,默认是由4个Space和多个辅助数据结构组成的。4个Space分别是Zygote Space、Allocation Space、Image Space、Large Object Space。Zygote Space、Allocation Space和DVM是一样的。Image Space是用来存放 一些预加载类,Large Object Space用来分配一些大对象。其中 Zygote Space和Image Space是进程间共享的。除了这四个Space,ART的Java堆中还包括两个Mod Union Table,一个Card Table、两个Heap Bitmap,两个Object Map,以及三个Object Stack。
谢谢阅读。
Java JVM
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。