文档章节

进入Android Dalvik虚拟机之Dalvik虚拟机的特点

柳哥
 柳哥
发布于 2015/01/08 18:51
字数 2223
阅读 1460
收藏 5

Google于2007年底正式发布了Android SDK,Dalvik虚拟机也第一次进入了人们的视野。它的作者是丹.伯恩斯坦(Dan Bornstein)。Dalvik虚拟机作为Android平台的核心组件,拥有如下几个特点:

  • 体积小,占用内存空间小

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

  • 常量池采用32位索引值,寻址类方法名,字段名,常量更快

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

  • 提供了对象生命周期管理,堆栈管理,线程管理,安全和异常管理以及垃圾回收等重要功能

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

1. Dalvik虚拟机与Java虚拟机的区别

Dalvik虚拟机与传统的Java虚拟机有着许多不同点,两者并不兼容,它们显著的不同点主要表现在以下几个方面:

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

  • Dalvik可执行文件体积小。Android SDK中有一个叫dx的工具负责将Java字节码转换为Dalvik字节码。dx工具对Java类文件重新排列,消除在类文件中出现的所有冗余信息避免虚拟机在初始化时出现反复的文件加载与解析过程。一般情况下,Java类文件中包含多个不同的方法签名,如果其他的类文件引用该类文件中的方法,方法签名也会被复制到其类文件中,也就是说,多个不同的类会同时包含相同的方法签名,同样地,大量的字符串常量在多个类文件中也被重复使用。这些冗余信息会直接增加文件的体积,同时也会严重影响虚拟机解析文件的效率。消除其中的冗余信息,重新组合形成一个常量池,所有的类文件共享同一个常量池。由于dx工具对常量池的压缩,使得相同的字符串,常量在DEX文件中只出现一次,从而减小了文件的体积。

  • Java虚拟机与Dalvik虚拟机架构不同。Java虚拟机基于栈架构,程序在运行时虚拟机需要频繁的从栈上读取或写入数据,这个过程需要更多的指令分派与内存访问次数,会耗费不少CPU时间,对于像手机设备资源有限的设备来说,这是相当大的一笔开销。Dalvik虚拟机基于寄存器架构。数据的访问通过寄存器间直接传递,这样的访问方式比基于栈方式要快很多。测试代码如下:

package t1;

public class Hello {
    public static void main(String[] args) {
        Hello hello = new Hello();
        System.out.println(hello.foo(5, 3));
    }
    public int foo(int a,int b) {
        return (a + b) * (a - b);
    }
}

将以上内容保存为Hello.java。打开命令提示符,执行命令:

$ javac -source 1.6 -target 1.6 Hello.java

:如果使用1.7及以上版本的JDK编译Hello.java,生成Hello.class默认的版本会比较低使用dx生成dex文件会提示class文件无效。解决方法是强制指定class文件的版本。

继续上面的讨论,执行上面的命令生成Hello.class文件。然后执行命令:

./dx --dex --output=t1/Hello.dex t1/Hello.class

执行上面的命令前,命令行进入到dx工具所在的目录(位于Android SDK的platform-tools目录中),再把t1/Hello.class目录与文件copy到dx工具所在的目录,然后再执行上面的命令生成dex文件。

接下来在Hello.class所在目录使用javap反编译Hello.class查看foo()函数的Java字节码,执行以下命令:

$ javap -c -cp . Hello.class

命令执行后得到如下代码(foo()函数部分):

public int foo(int, int);
    Code:
       0: iload_1
       1: iload_2
       2: iadd
       3: iload_1
       4: iload_2
       5: isub
       6: imul
       7: ireturn

下面再使用dexdump(位于Android sdk的platform-tools目录中)查看foo()函数的Dalvik字节码,执行以下命令:

$ ./dexdump -d t1/Hello.dex

命令执行后整理输出结果,可以得到如下代码(foo()函数部分):

000198:                                             |[000198] t1.Hello.foo:(II)I
0001a8: 9000 0304                                   |0000: add-int v0, v3, v4
0001ac: 9101 0304                                   |0002: sub-int v1, v3, v4
0001b0: b210                                        |0004: mul-int/2addr v0, v1
0001b2: 0f00                                        |0005: return v0

查看上面的Java字节码,发现foo()函数一共占用了8个字节,代码中每条指令占用1个字节。比起Java虚拟机字节码,上面的Dalvik字节码显得简洁很多,只有4条指令就完成了下面的操作。

通过上面的分析 可以发现,基于寄存器架构的Dalvik虚拟机与基于栈架构的Java虚拟机相比,由于生成的代码指令减少了,程序执行速度会更快一些

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

Android系统由Linux内核,函数库,Android运行时,应用程序框架以及应用程序组成。Dalvik虚拟机属行Android运行时环境,它与一些核心库共同承担Android应用程序的运行工作。

Android系统启动加载完内核后,第一个执行的是init进程,init进程首先要做的是设备的初始化工作,然后读取inic.rc文件并启动系统中的重要外部程序 Zygote。Zygote进程是Android所有进程的孵化器进程,它启动后会首先初始化Dalvik虚拟机,然后启动system_server并进入Zygote模式,通过socket等候命令。当执行一个Android应用程序时,system_server进程通过Binder IPC方式发送命令给Zygote,Zygote收到命令后通过fork自身创建一个Dalvik虚拟机的实例来执行应用程序的入口函数,这样一个程序就启动完成了。

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

  • fork(),创建一个Zygote进程(这种方式实际不会被调用)

  • forkAndSpecialize(),创建一个非Zygote进程

  • forkSystemServer(),创建一个系统服务进程。

其中,Zygote进程可以再fork()出其他进程,非Zygote进程则不能fork其他进程,而系统服务进程在终止后它的子进程也必终止。当进程fork成功后,执行的工作就交给了Dalvik虚拟机。Dalvik虚拟机首先通过loadClassFromDex()函数完成类的装载工作,每个类被成功解析后都会拥有一个ClassObject类型的数据结构存储在运行时环境中,虚拟机使用gDvm.loadedClasses全局哈希表来存储与查询所有装载进来的类,随后,字节码验证器使用dvmVerifyCodeFlow()函数对装入的代码进行校验,接着虚拟机调用FindCass()函数查找并装载main方法类,随后调用dvmInterpret()函数初始化解释器并执行字节码流。

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

JIT(Just-in-time Compilation,即时编译),又称为动态编译,是一种通过在运行时将字节码翻译为机器码的技术,使得程序的执行速度更快。Android2.2版本系统的Dalvik虚拟机引入了JIT技术,官方宣称新版的Dalvik虚拟机比以往执行速度快3~6倍。主流的JIT包含两种字节码编译方式

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

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

method方式很好理解,那什么是trace方式呢?在函数中一般很少是顺序执行代码的,多数的代码都分成了好几条执行路径,其中函数的有些路径在实际运行过程中是很少被执行的,这部分路径被称为“冷路径”,而执行比较频繁的路径被称为“热路径”。采用传统的method方式会编译整个方法的代码,这会使得在“冷路径”上浪费很多编译时间,并且耗费更多的内存;trace方法编译则能够快速地获取“热路径”代码,使用更短的时间与更少的内存来编译代码

目前,Dalvik虚拟机默认采用trace方式编译代码,同时也支持采用method方式来编译。

© 著作权归作者所有

柳哥
粉丝 207
博文 405
码字总数 347782
作品 0
杭州
技术主管
私信 提问
[学习笔记一]、Android体系与系统架构

1、Android系统架构 2、Linux Linux层,Android最底层最核心的部分。我们打款手机Setting,选择about phone选项,这一选项所显示的内核版本,就是我们所用的Linux内核的版本。 Linux层包含了A...

2tman
2015/11/20
62
0
Android和iOS的区别(从开发角度比较)

一、两者运行机制不同 1. iOS采用的是沙盒运行机制 (1)沙盒机制 出于安全考虑,iPhone对于安装在上面的应用程序有所限制,这个限制就是应用程序只能在为该改程序创建的文件系统中读取文件,...

安然若知
2018/06/28
0
0
android -------- java虚拟机和Dalvik虚拟机

java虚拟机 虚拟机是一种抽象化的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机有自己完善的硬体架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。Java虚...

切切歆语
2018/04/29
56
0
Android dvm的进程和Linux的进程, 应用程序的进程是否为同一个概念

DVM指dalivk的虚拟机。每一个Android应用程序都在它自己的进程中运行,都拥有一个独立的Dalvik虚拟机实例。 而每一个DVM都是在Linux 中的一个进程,所以说可以认为是同一个概念。 什么是and...

天王盖地虎626
01/12
9
0
Android程序是以独立的用户身份运行的吗?

正在阅读《Andoird - 移动开发一本就够》,作者在介绍Android系统的时候,画了一张Android体系结构的图表:每个Android App运行的时候都会创建一个Dalvik虚拟机实例,每一个Dalvik虚拟机实例...

虫虫
2011/09/27
864
4

没有更多内容

加载失败,请刷新页面

加载更多

IT兄弟连 HTML5教程 HTML5表单 新增的表单属性1

HTML5 Input表单为<form>和<input>标签添加了几个新属性,属性如表1。 1 autocomplete属性 autocomplete属性规定form或input域应该拥有自动完成功能,当用户在自动完成域中开始输入时,浏览器...

老码农的一亩三分地
40分钟前
5
0
OSChina 周五乱弹 —— 葛优理论+1

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @这次装个文艺青年吧 :#今日歌曲推荐# 分享米津玄師的单曲《LOSER》: mv中的舞蹈诡异却又美丽,如此随性怕是难再跳出第二次…… 《LOSER》-...

小小编辑
今天
1K
16
nginx学习笔记

中间件位于客户机/ 服务器的操作系统之上,管理计算机资源和网络通讯。 是连接两个独立应用程序或独立系统的软件。 web请求通过中间件可以直接调用操作系统,也可以经过中间件把请求分发到多...

码农实战
今天
5
0
Spring Security 实战干货:玩转自定义登录

1. 前言 前面的关于 Spring Security 相关的文章只是一个预热。为了接下来更好的实战,如果你错过了请从 Spring Security 实战系列 开始。安全访问的第一步就是认证(Authentication),认证...

码农小胖哥
今天
15
0
JAVA 实现雪花算法生成唯一订单号工具类

import lombok.SneakyThrows;import lombok.extern.slf4j.Slf4j;import java.util.Calendar;/** * Default distributed primary key generator. * * <p> * Use snowflake......

huangkejie
昨天
19
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部