Java&Android 基础知识梳理(7) - Android 虚拟机

一、Dalvik 虚拟机

DalvikGoogle公司自己设计用于Android平台的Java虚拟机,它是Android平台的重要组成部分,支持dex格式的Java应用程序的运行。

Dalvik作为面向Linux、为嵌入式操作系统设计的虚拟机,主要负责完成 对象生命周期管理、堆栈管理、线程管理、安全和异常管理,以及垃圾回收等。Dalvik充分利用Linux进程管理的特定,对其进行了面向对象的设计,使得可以 同时运行多个进程,而传统的Java程序通常只能运行一个进程。

1.1 Dalvik 虚拟机和 JVM 的对比

区别一

  • 大多数的JVM虚拟机基于 栈的结构,基于栈的指令更紧凑,使用的指令只占用一个字节,因而成为字节码。
  • Dalvik虚拟机则是基于 寄存器,基于寄存器的指令由于需要指定源地址和目标地址,因此需要占用更多的指令空间,某些指令需要占用两个字节。

区别二

  • Java虚拟机运行的是Java字节码。Java类会被编译成一个或者多个.class文件,然后打包到jar文件中,接着Java虚拟机会从相应的.class文件和.jar文件中获取对应的字节码。
  • Dalvik虚拟机运行的是.dex文件。在Java类被编译成.class文件后,还会通过dx工具将所有的.class文件转换一个.dex文件,Dalvik虚拟机再从中读取指令和数据。.dex文件除了减少整体的文件尺寸和I/O操作次数,也提高了类的查找速度。

区别三

  • class文件中包含多个不同的方法签名,如果A类文件引用B类文件中的方法,方法签名也会被复制到A类文件中(在虚拟机加载类的连接阶段将会使用该签名链接到B类的对应方法),也就是说,多个不同的类会同时包含相同的方法签名,同样地,大量的字符串常量在多个类文件中也被重复使用,这些冗余信息会直接增加文件的体积,而JVM在把描述类的数据从.class文件加载到内存时,需要对数据进行校验、转换解析和初始化,最终才形成可以被虚拟机直接使用的JAVA类型,因为大量的冗余信息,会严重影响虚拟机解析文件的效率。
  • .dex文件中,由于dx工具会对JAVA类文件重新排列,将所有JAVA类文件中的常量池分解,消除其中的冗余信息,重新组合形成一个常量池,所有的类文件共享同一个常量池,使得相同的字符串、常量在.dex文件中只出现一次,从而减小了文件的体积。

1.2 Dalvik 虚拟机特点

  • 使用dex格式的字节码,不兼容Java字节码格式
  • 代码密度小,运行效率高,节省资源
  • 常量池只使用32位的索引
  • 有内存限制
  • 默认栈大小是12KB
  • 堆默认启动大小为2MB,默认最大值为16MB
  • 堆支持的最小启动大小为1MB,支持的最大值为1024MB
  • 堆和栈参数可以通过-Xms-Xmx修改

1.3 Dalvik 系统架构

1.3.1 dex 文件结构

Java&Android 基础知识梳理(7) - Android 虚拟机_第1张图片
.class 文件与 .dex 文件对比

.dex文件结构和.class文件结构差异的地方很多,但从携带的信息上看,.dex.class文件是一致的:

  • header:存储了各个数据类型的起始地址、偏移量等信息。
  • proto_ids:描述函数原型信息,包括返回值,参数信息。比如“test:()V”
  • methods_ids:函数信息,包括所属类及对应的proto信息。

虽然.dex文件的结构很紧凑,但想要运行时的性能得到进一步提升,还需要对dex文件进行进一步优化。优化主要针对以下几个方面:

  • 调整所有字段的字节序和对齐结构中的每一个域
  • 验证.dex文件中的所有类
  • 对一些特定的类进行优化,对方法里的操作码进行优化

.dex文件经过优化后文件大小会膨胀,大约增加到原来的1~4倍。对于内置应用,一般在系统编译后,便会生成优化文件odex(Optimized dex)。一个Android应用程序,需要经过以下过程才可以在Dalvik虚拟机上运行:

  • Java源文件编译成.class文件
  • 使用dx工具把.class文件转换成.dex文件
  • 使用aapt工具把.dex文件、资源文件以及AndroidManifest.xml文件组合成APK
  • APK安装到Android设备运行
Java&Android 基础知识梳理(7) - Android 虚拟机_第2张图片
.apk 文件的产生过程

1.3.2 Dalvik 类加载器

一个dex文件需要类加载器加载原生类和Java类,然后通过解释器根据指令集对Dalvik字节码进行解释和执行。Dalvik类加载器使用mmap函数,将dex文件映射到内存中,通过普通的内存读取操作即可访问dex文件,然后解析dex文件内容并加载其中的类到哈希表中。

解析 dex

总的来说,dex文件可以抽象为三个部分:头部、索引、数据。通过头部可以知道索引的位置和数目,以及数据区的起始位置。将dex文件映射到内存后,Dalvik会调用dexFileParse函数对其进行分析,分析的结果放到DexFile数据结构中。DexFile中的baseAddr指向映射区的起始位置,pClassDefs指向class索引的起始位置。为了加快class的查找速度,还创建一个哈希表,对class名字进行哈希并生成索引。

加载 class

解析工作完成后就进行class的加载,加载的类需要用ClassObject数据结构来存储。

typedef struct Object {
    ClassObject* clazz;  // 类型对象
    Lock lock;           // 锁对象
} Object;

其中clazz指向ClassObject对象,还包含一个Lock对象。如果其它线程想要获取它的锁,只有等这个线程释放。Dalvik每加载一个class都会对应一个ClassObject对象,加载过程会在内存中分配几个区域,分别存放directMethodvirtualMethodsfieldifield。这些信息从dex文件的数据区中读取。字段Field的定义如下:

struct Field {
    ClassObject* clazz;    //所属类型
    const char* name;      // 变量名称
    const char* signature; // 如“Landroid/os/Debug;”
    u4 accessFlags;        // 访问标记
    
    #ifdef PROFILE_FIELD_ACCESS
        u4 gets;
        u4 puts;
    #endif
};

待得到class索引后,实际的加载由loadClassFromDex来完成。首先它会读取class的具体数据,分别加载directMethodvirtualMethodifieldsfield,然后为ClassObject数据结构分配内存,并读取dex文件的相关信息。加载完成后,将加载的class通过dvmAddClassToHash函数放入哈希表,以方便下次查找;最后,通过dvmLinkClass查找该类的超类,如果有接口类则加载相应的接口类。

1.3.3 Dalvik 解释器

对于任何虚拟机来说,解释器无疑是核心的部分,所有的Java字节码都经过解释器解释执行。由于Dalvik解释器的效率很重要,Android分别实现了C语言版和各种汇编语言版的解释器。解释器通常是循环执行,需要一个入口函数调用处理程序执行第一条指令,而后每条指令执行时引出下一条指令,通过函数指针调用处理程序。

二、Dalvik 虚拟机和 ART 虚拟机对比

Android 4.4之后,Google开始使用了更加优秀的ART虚拟机来替换Dalvik虚拟机,下面我们就来对比一下这两者之间的区别。

2.1 Dalvik

打包的过程中 会先将.java等源码通过javac编译成.class文件,再通过dx.class文件转换成Dalvik虚拟机执行的.dex文件。

应用启动的时候 先将.dex文件 转换成机器码,又因为65536的文件,导致在应用冷启动的时候有一个合包的过程,最后的结果就是app的启动时间有可能变慢,这就是Dalvik虚拟机的JIT(Just in Time)特性。

2.2 ART

ART除了兼容了Dalvik虚拟机的特性之外,还有一个很好的特性AOT(Ahead of Time),这个特性就是把 .dex 文件转换成机器码 这个步骤提前到了 应用安装 的时候,ART虚拟机将.dex文件转换成可直接运行的.oat文件,ART虚拟机天生支持多dex,所以也不会有一个合包的过程,因此会极大的提升APP冷启动速度。

2.3 ART 虚拟机的优缺点

优点:

  • 加快APP冷启动速度
  • 提升GC速度
  • 提供功能全面的Debug特性

缺点:

  • APP安装速度慢,因为在APK安装的时候要生成可运行.oat文件
  • APK占用空间大,因为在APK安装的时候要生成可运行.oat文件

三、参考文献

理解 Android 虚拟机体系结构
Android Dalvik 虚拟机和 ART 虚拟机对比

Dalvik 虚拟机简要介绍和学习计划
Dalvik 虚拟机的启动过程分析
Dalvik 虚拟机的运行过程分析

深入理解 Dalvik 虚拟机 - Android应用进程启动过程分析
深入理解 ART 虚拟机 - 虚拟机的启动
深入理解 ART 虚拟机 - ART 的函数运行机制
深入理解 Dalvik 虚拟机 - 解释器的运行机制
深入理解ART虚拟机 - ImageSpace的加载过程分析


更多文章,欢迎访问我的 Android 知识梳理系列:

  • Android 知识梳理目录:http://www.jianshu.com/p/fd82d18994ce
  • Android 面试文档分享:http://www.jianshu.com/p/8456fe6b27c4

你可能感兴趣的:(Java&Android 基础知识梳理(7) - Android 虚拟机)