Android Dalvik虚拟机

估计很多人和我一样,在java学习和android学习过程中经常会见到JVM和Dalvik虚拟机这两个名字,特别是由于Dalvik虚拟机,我对其了解

一直处于比较片面的状态。没有从远处整体的看过它是一个什么东西,以及为什么会有这样的一个东西出来。

可能是为了解决移动设备上软件运行效率的问题,也可能是为了规避与(从jdk下载我们就知道了~)Oracle公司的版权纠纷,Google为Android平台专门设计了一套虚拟机来运行Android程序,他就是Dalvik虚拟机。

对于JVM的原理和机制这篇文章中我不系统的进行描述了,但是给出一篇链接,个人觉得写得还是不错的:http://blog.csdn.net/ning109314/article/details/10411495,有兴趣的话可以看一下,因为JVM和Dalvik虚拟机在很多地方还是有一定的相似之处的,趁着学习顺便把这两个东西都搞清楚不是更好?

1.Dalvik的基本特性

Dalvik虚拟机组委Android平台的核心组件,拥有如下几个特点:

1)体积小,占用内存空间小;

2)专有的DEX可执行文件格式,体积更小,执行速度更快;

3)常量池采用32位索值,寻址类方法名、字段名;

4)基于寄存器架构,并拥有一套完整的指令系统;

5)提供了对象声明周期管理、堆栈管理、线程管理、安全和异常管理以及垃圾回收等重要功能;

6)所有的Android程序都运行在Android系统进程里,每个进程都对应一个Dalvik虚拟机实例。

2.Dalvik虚拟机与JVM的区别

 Dalvik虚拟机与传统的JVM有着许多不同的特点,两者并不兼容,他们显著的不同可以总结为以下几个方面:


1)JVM运行的是java字节码,而Dalvik虚拟机运行的是Dalvik字节码

java程序经过编译,生成java字节码保存在class文件之中,JVM通过解码class文件中的内容来运行程序。而Dalvik虚拟机运行的是dalvik字节码,所有的Dlavik字节码由Java字节码转换而来,并被打包到一个DEX(Dalvik Executable)可执行文件中。Dalvik虚拟机通过解释DEX文件来执行这些字节码。

2)Dalvik可执行文件体积比JAVA类文件更小(相同外部条件的假设下)

SDK中有一种叫dx的工具负责将Java字节码转换为Dalvik字节码,dx工具对java class文件重新排列,消除在类文件中出现的所有冗余信息,避免虚拟机在初始化时

出现反复文件加载与解析的过程。一般情况下,Java类文件中包含多个不同的方法签名,如果其他的类文件引用该类文件中的方法,方法签名也会被复制到其类文件中,也就是说多个不同类会同时包含相同的方法签名,同样的,大量的字符串常量在多个类文件中也被重复使用,这些冗余信息直接增加文件的体积,同时也会严重影响JVM的解释效率。

dx就是针对这个问题而做的工具,它将所有的Java类文件中的常量池进行分解,消除其中的冗余信息,重新组合形成一个常量池,所有的类文件共享一个常量池。

来看一张直观的图理解一下:


3)JVM和Dalvik虚拟机的架构不同

JVM是基于栈架构的,而Dalvik是基于寄存器架构的,有v寄存器(命名法)和p寄存器(命名法),如v0 v1...和p0 p1...。

JVM在程序运行时需要频繁的从栈上读取和写入数据,这个过程需要更多的指令分派与内存访问次数,会耗费不少CPU时间,对于移动设备这种资源十分有限的

设备来说是相当大的一笔开销。

Dalvik虚拟机是基于寄存器架构的,数据访问通过寄存器直接传递,这样的访问方式比基于栈方式快的多。

其实JVM的栈结构和X86 Linux下函数的栈和栈帧结构很类似,可以对照着理解。

我们重点来说Dalvik,Dalvik虚拟机基于寄存器架构,在代码中大量的使用了寄存器,Dalvik虚拟机是作用于特定架构的CPU上运行的,在设计之初,Dalvik是运行在ARM

架构的,ARM架构本身就有多个寄存器,Dalvik将部分寄存器直接映射到ARM寄存器上,还有一部分则通过调用需要2个栈进行模拟。Dalvik中用到的寄存器都是32位的,支持64位,但是需要两个相邻的寄存器来表示。

3.Dalvik虚拟机是如何执行程序的

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

2)Zygote进程是Android所有进程的孵化器进程,它启动后会首先初始化Dalvik虚拟机,然后启动system_server并进入Zygote模式,通过socket等待命令。

3)当执行一个Android应用程序时,system_server进程通过socket方式发送命令给Zygote,Zygote收到命令后通过fork自身创建一个Dalvik虚拟机实例来执行应用程序

的入口函数,这样一个程序就启动完成了。


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

fork():创建一个Zygote进程

forkAndSpeacialize():创建一个非Zygote进程

forkSystemServer():创建一个系统服务进程

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



插一段广告吧(因为我忘了这两个的区别了~~顺便查了查):

1.  fork  ():子进程拷贝父进程的数据段,代码段 
    vfork ( ):子进程与父进程共享数据段 
2.  fork ()父子进程的执行次序不确定 
    vfork 保证子进程先运行,在调用exec 或exit 之前与父进程数据是共享的,在它调用exec
     或exit 之后父进程才可能被调度运行。 
3.  vfork ()保证子进程先运行,在她调用exec 或exit 之后父进程才可能被调度运行。如果在
   调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。 

广告回来:

刚说到当fork成功后,执行的工作的就交给了Dalvik虚拟机,Dalvik虚拟机首先通过loadClassFromDex()函数完成类的装载工作,每个类被成功

解析后都会拥有一个ClassObject类型的数据结构存储在运行时环境中,虚拟机使用gDvm.loadedClass全局哈希表来存储与查询所有装载进来的类,

随后,字节码验证器使用dvmVerifyCodeFlow()函数对装入的代码进行校验,接着虚拟机调用FindClass()函数查找并装载main方法类,随后调用

dvmInterpret()函数初始化解释器并执行字节码流。这个过程的流程图如下:


4.关于Dalvik虚拟机的JIT(Just-in-time Compilation,即时编译)

JIT,即时编译,也叫动态编译,是一种通过在运行时将字节码翻译为机器码的技术,使得程序的执行速度更快的技术。

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

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

trace方式:以trace为单位进行编译

method方式大家 根据名字都能判断出来是怎样的一种编译方式,很好理解。

然而trace方式就需要大概解释一下了,一般在程序中,有一些代码是很少被执行的,这种逻辑路径称为“冷路径”,而执行比较频繁的路径被称为“热路径”。如果使用method方法的话需要编译整个方法,因此比较耗时(CPU)和内存,而trace方式能够快速的获取“热路径”代码,使用更短的时间与更少的内存来编译代码。(这种思想其实与unlike函数,和likely函数来提高CPU预取指令效率的思想差不多)。

5.Dalvik指令集

Dalvik虚拟机有专门的一套指令集,并且指定了自己的指令格式和调用规范,关于Dalvik指令集的详细内容我不想再一行一行码上去了,毕竟都是些死的东西。大家可以自行Google。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值