认识Dalvik虚拟机

Posted by 彩笔学长 on October 20, 2016

概述

虽然Android平台使用java开发,但是android程序去不是运行在标准java虚拟机上,为了解决移动设备软件运行效率问题及规避Oracle公司版权,Google为android平台专门设计一套虚拟机来运行Android程序,即Dalvik Virtual Manchine(Dalvik 虚拟机),它的作者是Dan Bornstein,名称由来于他祖先曾居住过的名叫Dalvik的小渔村

Dalvik虚拟机的特点

主要特点

  • 体积小
  • 专有的DEX可执行文件,体积小执行速度快
  • 常量池采用32位索引值,寻址类方法名称、字段名、常量更快
  • 基于寄存器架构,拥有自己的一套完整指令系统
  • 提供了对象生命周期管理、堆栈管理、线程管理、安全和异常管理以及垃圾回收等重要功能
  • 所有的Android程序都运行在Android系统进程里,每个进程都对应着一个Dalvik虚拟机实例

Dalvik虚拟机与java虚拟机区别

  • 1、Java虚拟机运行的是Java字节码,Dalvik运行的是Dalvik字节码

所有的Dalvik字节码由Java字节码转换而来,并被打包到一个DEX可执行文件,Dalvik虚拟机通过解释DEX文件来执行这些字节码

  • 2、Dalvik可执行文件体积更小

Android SDK中有一个叫dx的工具负责将java字节码转换为Dalvik字节码。dx工具将Java类文件重新排列,消除类文件中出现的所有冗余信息,避免虚拟机初始化时出现重复的文件加载与解析过程。

(注:一般情况下,Java类文件包含多个不同的方法签名,如果其他的类文件引用该类文件中的方法,方法签名也会复制到其中,就会造成多个不同的类会同时包含相同的方法签名,同样的,大量的字符串常量在多个类文件中也会被重复使用,这些冗余信息会直接增加文件的体积,),同时也会严重影响虚拟机解析文件的效率。

dx工具针对这个做了专门的处理,它将所有java类文件中常量池进行分解,消除其中的冗余信息,重新形成一个常量池,所有类文件共享同一个常量池,从而减小了体积

  • 3、Java虚拟机与Dalvik虚拟机架构不同

架构不同

Java虚拟机基于栈架构,程序在运行时虚拟机需要频繁的从栈上读取或者写入数据,该过程需要更多的指令分派和内存访问,耗费CPU时间 ,Dalvik虚拟机基于寄存器架构,数据访问通过寄存器间接传递,这样访问要比基于栈的方式要快很多。

虚拟机运行状态不同

Java虚拟机的指令集被称为零地址形式的指令集,所谓零地址形式,是指指令的源参数与目标参数都是隐含的,通过Java虚拟机中提供的一种数据结构“求值栈”来传递。对于Java程序来说,每个线程在执行时都有一个PC计数器与一个Java栈,PC计数器只对当前方法有效,Java虚拟机通过它的值来取指令执行,Java栈用于记录java方法调用的“活动记录”,Java栈以帧为单位保存线程的运行状态

第一条指令 iload_1 表示将第二个int类型局部变量进栈

第三条指令 iadd 从栈顶弹出两个int类型值,将值相加,然后把结果压回栈顶

第四、五条指令 分别压入第一个和第二个参数

第六条指令 isub 从栈顶弹出两个int类型值,将值相减,然后把结果压回栈顶

第七条指令 imul从栈顶弹出两个int型值,将两值相乘,然后把结果压回栈顶

第八条指令 ireturn返回一个int值

Dalvik虚拟机运行时同样为每个线程维护一个PC计数器与调用栈,与Java虚拟机不同的是,这个调用栈维护一份寄存器列表,同样的程序Dalvik命令明显减少

所处位置

Android 系统架构采用分层思想,Dalvik虚拟机位于Android运行时环境,它与一些核心库共同承担Android应用程序的运行工作

android启动加载完内核后,第一个执行的是init进程,init进程首先要做的是设备初始化,然后读取init.rc文件并启动系统中的重要的外部程序Zygote。

Zygote进程是Android所有进程的孵化器进程,它启动后会首先初始化Dalvik虚拟机,然后启动system_server并进入Zygote模式,通过socket等候命令。当执行一个Android应用程序时,system_server进程通过socket方式发送命令给zygote,zygote收到命令之后通过fork自身创建一个Daalvik虚拟机的实例来执行应用程序的入口程序,这样一个程序就启动完了

其中Zygote提供了三种创建进程的方法

  • fork(),创建一个Zygote进程
  • forkAndSecialize(),创建一个非Zygote进程
  • forkSystemSever(),创建一个系统服务进程

其中Zygote进程可以再fork出其他进程,非zygote进程则不能fork其他进程,而服务系统的进程终止后它的子进程也必须终止。

关于Dalvik虚拟机JIT(即时编译)

JIT 又称动态编译,是一种通过在运行时将字节码翻译为机器码的技术,使得程序的执行速度更快,在Android2.2版本的Dalvik虚拟机引入了JIT技术

主流的JIT包含两种字节码编译方式

  • method方式 以函数或者方法为单位进行编译
  • trace方式 以trace为单位进行编译

注:trace是指:在函数中一般很少顺序执行代码的,多数代码都被分为好几条执行路径,其中有的函数路径实际过程很少被执行的,这部分被称为冷路径,而执行比较频繁的被称为热路径,采用传统的method方式会编译整个方法的代码,这会使得冷路径上浪费很多编译时间,并且耗费更多的内存:trace方式编译则能够快速的获取热路径代码,使用更短的时间与更少的内存来编译代码,目前Dalvik虚拟机默认采用trace方式编译代码同时也支持method方式。