知识点、
一、安卓虚拟机的发展历史
1、诞生
每次运行程序的时,Dalvik VM负责加载 dex/odex 文件并解析成机器码交由系统调用。
2、Android 2.2 JIT 登场
和其他大多数 JVM 一样,Dalvik 使用 JIT 进行即时编译,借助 Java HotSpot VM,JIT 编译器可以对热点代码进行编译优化,将 dex/odex 中的 Dalvik Code ( Smali 指令集 ) 翻译成相当精简的 Native Code 去执行,JIT 的引入使得 Dalvik 的性能提升了 3~6 倍。
3、Android 4.4 ART 和 AOT 登场
在 Android 4.4 带来了全新的虚拟机运行环境 ART 和全新的编译策略 AOT,此时 ART 和 Dalvik 是共存的。
4、Android 5.0 ART 全面取代 Dalvik
此时Dalvik VM退出历史舞台,AOT 也成为了唯一的编译方式。
5、Android 7.0 JIT 回归
形成了 AOT / JIT 混合编译模式。该混合模式综合了 AOT 和 JIT 的各种优点,使得应用在安全速度加快的同时,运行速度、存储空间和耗电量等指标都得到了优化。
二、DVM与JVM区别
1、基于的架构不同
JVM基于栈,要去栈中读写数据,在复制数据时而使用大量的出入栈指令。所需的指令会更多。对于性能有限的移动设备样会导致速度变慢。
DVM是基于寄存器的,指令更紧凑、更简洁。由于显式指定了操作数,所以基于寄存器的指令会比基于栈的指令要大,但是由于指令数量的减少,总的代码数不会增加多少。
2、执行的字节码不同
java源文件经过javac编译器编译成class文件,jvm执行的为class文件
java源文件经过javac编译器编译成class文件,而class文件经过dx工具被编译成dex文件,dvm执行的为dex文件。
问题:安卓dvm为啥这样设计,向jvm那样执行class文件不好吗?
每个class文件里面包含了该类的常量池、类信息、属性等。当JVM加载多个class文件的时候,会加载里面的所有的class文件,JVM的这种加载方式很慢,对于内存有限的移动设备并不合适。而在apk文件中只包含了一个dex文件,这个dex文件将所有的class里面所包含的信息全部整合在一起了,这样再加载就加快了速度。
3、DVM允许在有限的内存中同时运行多个进程
Android中的每一个应用都运行在一个DVM 实例中,每一个DVM 实例都运行在一个独立的进程空间中,独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。
4、DVM由Zygote创建和初始化
Zygote是一个DVM进程,同时也用来创建和初始化DVM实例。
每当系统需要创建一个应用程序时,Zygote就会fock自身,快速地创建和初始化一个DVM实例,用于应用程序的运行。
对于一些只读的系统库,所有的DVM实例都会和Zygote共享一块内存区域,节省了内存开销。
5、DVM有共享机制
安卓基于linux系统,应用进程之间可以通过共享内存进行共享内存数据,而不同的java程序运行在不同的jvm进程中,jvm运行在操作系统上,jvm未提供共享机制。
6、DVM早期没有使用JIT编译器
早期的DVM每次执行代码,都需要通过解释器将dex代码编译成机器码,然后交给系统处理,效率不是很高。
Android 2.2版本开始DVM使用了JIT 编译器,它会对多次运行的代码(热点代码)进行编译,生成相当精简的本地机器码(Native Code),这样在下次执行到相同逻辑的时候,直接使用编译之后的本地机器码,而不是每次都需要编译。
需要注意的是,应用程序每一次重新运行的时候,都要重做编译工作,因此每次重新打开应用程序,都需要JIT编译
三、DVM的架构
Java编译器编译的.class 文件经过DX工具转换为.dex文件,dex文件由类加载器处理,接着解释器根据指令集对Dalvik字节码进行解释、执行,最后交于Linux处理。
四、 DVM的运行时堆
DVM的运行时堆gc算法:标记—清除(Mark-Sweep)算法
DVM的运行时堆组成:
- Zygote Space(Zygote Heap)
- Allocation Space (Active Heap)
- Card Table:用于DVM Concurrent GC,当第一次进行垃圾标记后,记录垃圾信息
- Heap Bitmap:有两个Heap Bitmap,一个用来记录上次GC存活的对象,另一个用来记录这次GC存活的对象。
- Heap Bitmap:有两个Heap Bitmap,一个用来记录上次GC存活的对象,另一个用来记录这次GC存活的对象。
在Zygote进程fork第一个子进程之前,会把Zygote Space分为两个部分,原来的已经被使用的那部分堆仍旧叫Zygote Space,而未使用的那部分堆就叫Allocation Space,以后的对象都会在Allocation Space上进行分配和释放。
五、ART与DVM区别
1、运行效率
运行效率的区别其实是AOT与JIT编译器的区别。
DVM:应用每次运行时,字节码都需要通过JIT编译器编译为机器码,这会使得应用程序的运行效率降低。
ART:
系统在安装应用程序时会进行一次AOT(ahead of time compilation,预编译),将字节码预先编译成机器码并存储在本地,这样应用程序每次运行时就不需要执行编译了,运行效率会大大提升,设备的耗电量也会降低
2、CUP设计
DVM:为32位CPU设计
ART:支持64位并兼容32位CPU
3、垃圾回收
ART相对DVM:ART对垃圾回收进行了改进,更频繁地执行并行垃圾收集,将GC暂停由2次减少为1次等。
4、运行时控件堆的划分
ART采用了多种垃圾收集方案,默认是采用标记清除法(CMS)
ART的运行时堆的空间默认是由4个Space和多个辅助数据结构组成。
(1)4 Space
- Zygote Space:与DVM一致
- Allocation Space:与DVM一致
- Image Space:存放一些预加载类,进程间共享
- Large Object Space:分配一些大对象(默认大小为12KB)
(2)多个数据结构
- 两个Mod Union Table
- 一个CardTable
- 两个Heap Bitmap
- 两个Object Map
- 三个Object Stack
AOT与JIT的区别
1、AOT优缺点
优点:
-
应用启动快,运行更加流畅。
-
耗电问题得到优化
缺点:
- 安装应用时需要预编译相对耗时
- 优化后的文件占额外的空间
2、JIT优缺点
优点:
- 热点代码得到编译
缺点:
-
每次应用启动都需要编译
-
运行时更加耗电
The end
参考 :
安卓进阶解密:微信读书版
https://github.com/Omooo/Android-Notes/blob/master/blogs/Android/Dalvik%20%E5%92%8C%20ART.md