Google于2007年底正式发布了Android SDK, 作为 Android系统的重要特性,Dalvik虚拟机也第一次进入了人们的视野。它对内存的高效使用,和在低速CPU上表现出的高性能,确实令人刮目相看。 依赖于底层Posix兼容的操作系统,它可以简单的完成进程隔离和线程管理。每一个Android应用在底层都会对应一个独立的Dalvik虚拟机实例, 其代码在虚拟机的解释下得以执行。
很多人认为Dalvik虚拟机是一个Java虚拟机,因为Android的编程语言恰恰就是Java语言。但是这种说法并不准确,因为Dalvik虚拟机并不是按照Java虚拟机的规范来实现的,两者并不兼容;同时还要两个明显的不同:
- Java虚拟机运行的是Java字节码,而Dalvik虚拟机运行的则是其专有的文件格式DEX(Dalvik Executable)。
- 在Java SE程序中的Java类会被编译成一个或者多个字节码文件(.class)然后打包到JAR文件,而后Java虚拟机会从相应的CLASS文件和JAR文 件中获取相应的字节码;Android应用虽然也是使用Java语言进行编程,但是在编译成CLASS文件后,还会通过一个工具(dx)将应用所有的 CLASS文件转换成一个DEX文件,而后Dalvik虚拟机会从其中读取指令和数据。
Dalvik和Android系统Android作为新一代的基于Linux的开源手机操作系统,其系统架构由下而上可以分为以下几部分:
- Linux内核
- 本地库
- Android运行库
- 应用框架
- 应用
java虚拟机和Dalvik虚拟机的区别:
java虚拟机 | Dalvik虚拟机 | |
java虚拟机基于栈。 基于栈的机器必须使用指令来载入和操作栈上数据,所需指令更多更多 | dalvik虚拟机是基于寄存器的 | |
java虚拟机运行的是java字节码。(java类会被编译成一个或多个字节码.class文件,打包到.jar文件中,java虚拟机从相应的.class文件和.jar文件中获取相应的字节码)
| Dalvik运行的是自定义的.dex字节码格式。(java类被编译成.class文件后,会通过一个dx工具将所有的.class文件转换成一个.dex文件,然后dalvik虚拟机会从其中读取指令和数据) | |
常量池已被修改为只使用32位的索引,以 简化解释器。dalvik的堆和栈的参数可以通过-Xms和-Xmx更改 | ||
一个应用,一个虚拟机实例,一个进程(所有android应用的线程都是对应一个linux线程,都运行在自己的沙盒中,不同的应用在不同的进程中运行。每个android dalvik应用程序都被赋予了一个独立的linux PID(app_*)) |
Dalvik虚拟机架构:
在android源码中,Dalvik虚拟机的实现位于“dalvik/”目录下,其中“dalvik/vm”是虚拟机的实现部分,将会编译成libdvm.so;而"dalvik/libdex"将会编译成libdex.a静态库作为dex工具;“dalvik/dexdump”是.dex文件的反编译工具;虚拟机的可执行程序位于“dalvik/dalvikvm”中,将会编译成dalvikvm可执行文件。
dalvik虚拟机架构:
Android应用编译及运行流程:
Dalvik进程管理:
dalvik进程管理是依赖于linux的进程体系结构的,如要为应用程序创建一个进程,它会使用linux的fork机制来复制一个进程(复制进程往往比创建进程效率更高)。
Zygote是一个虚拟机进程,同时也是一个虚拟机实例的孵化器,它通过init进程启动。首先会孵化出System_Server(android绝大多系统服务的守护进程,它会监听socket等待请求命令,当有一个应用程序启动时,就会向它发出请求,zygote就会FORK出一个新的应用程序进程).每当系统要求执行一个android应用程序时,Zygote就会运用linux的FORK进制产生一个子进程来执行该应用程序。
JVM和Dalvik进程管理:
linux中进程间通信的方式有很多,但是dalvik使用的是信号方式来完成进程间通信。
Android的初始化流程:
android基本架构
Android其本质就是在标准的Linux系统上增加了Java虚拟机Dalvik,并在Dalvik虚拟机上搭建了一个JAVA的application framework,所有的应用程序都是基于JAVA的application framework之上。
Android主要应用于ARM平台,但不仅限于ARM,通过编译控制,在X86、MAC等体系结构的机器上同样可以运行。
android分为四个层,从高层到低层分别是应用程序层、应用程序框架层、系统运行库层和linux核心层。
蓝色的代表java程序,黄色的代码为运行JAVA程序而实现的虚拟机,绿色部分为C/C++语言编写的程序库,红色的代码内核(linux内核+driver)。在Application Framework之下,由C/C++的程序库组成,通过JNI完成从JAVA到C的调用。
1) 应用程序
所有的应用程序都是使用JAVA语言编写的,每一个应用程序由一个或者多个活动组成,活动必须以Activity类为超类,活动类似于操作系统上的进程,但是活动比操作系统的进程要更为灵活,与进程类似的是,活动在多种状态之间进行切换。
利用JAVA的跨平台性质,基于Android框架开发的应用程序可以不用编译运行于任何一台安装有android系统的平台,这点正是Android的精髓所在。
2) 应用程序框架
应用程序的架构设计简化了组件的重用;任何一个应用程序都可以发布它的功能块并且任何其它的应用程序都可以使用其所发布的功能块(不过得遵循框架的安全性限制)。帮助程序员快速的开发程序,并且该应用程序重用机制也使用户可以方便的替换程序组件。
隐藏在每个应用后面的是一系列的服务和系统, 其中包括;
a.丰富而又可扩展的视图(Views),可以用来构建应用程序, 它包括列表(lists),网格(grids),文本框(text boxes),按钮(buttons), 甚至可嵌入的web浏览器。
b.内容提供器(Content Providers)使得应用程序可以访问另一个应用程序的数据(如联系人数据库), 或者共享它们自己的数据。
c.资源管理器(Resource Manager)提供非代码资源的访问,如本地字符串,图形,和布局文件(layout files)。
d.通知管理器(Notification Manager)使得应用程序可以在状态栏中显示自定义的提示信息。
e.活动管理器(Activity Manager)用来管理应用程序生命周期并提供常用的导航回退功能。
3) 系统运行库
a)程序库
Android包含一些C/C++库,这些库能被Android系统中不同的组件使用。它们通过Android应用程序框架为开发者提供服务。
以下是一些核心库:
主要包括基本的C库、以及多媒体库以支持各种多媒体格式、位图和矢量字体、2D和3D图形引擎、浏览器、数据库支持。
1.Bionic系统C库。
2.媒体库,基于PacketVideo OpenCORE。
3.Surface Manager 顾名思义,用于管理Surface。
4.Webkit,LibWebCore 浏览器,基于Webkit引擎。
5.SGL 底层的2D图形引擎
6.3D libraries 基于OpenGL ES 1.0 APIs实现
7.FreeType 位图(bitmap)和矢量(vector)字体显示。
8.SQLite 一个对于所有应用程序可用,功能强劲的轻型关系型数据库引擎。
另外这里还有一个硬件抽象层。其实Android并非所有的设备驱动都放在linux内核里面,有一部分实现在用户空间,
这么做的主要原因是可以避开Linux所遵循的GPL协议,一般情况下如果要将Android移植到其他硬件去运行,
只需要实现这部分代码即可。包括:显示器驱动,声音,相机,GPS,GSM等等。
b)Android 运行库
Android 包括了一个核心库,该核心库提供了JAVA编程语言核心库的大多数功能。
每一个 Android应用程序都在它自己的进程中运行,都拥有一个独立的Dalvik虚拟机实例。
Dalvik被设计成一个设备可以同时高效地运行多个虚拟系统。
Dalvik虚拟机执行(.dex)的Dalvik可执行文件,该格式文件针对小内存使用做了优化。
同时虚拟机是基于寄存器的,所有的类都经由JAVA编译器编译,然后通过SDK中的 "dx" 工具转化成.dex格式由虚拟机执行。
Dalvik虚拟机依赖于linux内核的一些功能,比如线程机制和底层内存管理机制。
4) Linux 内核
Android 的核心系统服务依赖于 Linux 2.6 内核 ,如安全性,内存管理,进程管理, 网络协议栈和驱动模型。
Linux 内核也同时作为硬件和软件栈之间的抽象层。其外还对其做了部分修改,主要涉及两部分修改:
a)Binder
(IPC):提供有效的进程间通信,虽然linux内核本身已经提供了这些功能,但Android系统很多服务都需要用到该功能,为了某种原因
其实现了自己的一套。
b)电源管理:为手持设备节省能耗。
最后在谈谈Android所采用的语言,其应用开发采用java语言,我们所说的java一般包含三个部分:
a) java语言:即其语法,其写代码的程式
b) java虚拟机:为了实现一次编译到处可以运行的原则,java在编译连接以后并没有产生目标机器语言,而是采用了Java bytecode
这种Java共用指令,这时就需要一个虚拟机来执行改指令。
c) 库:跟我们常用的C语言一样提供一些常用的库
后两者结合就是Java Runtime Environment。
参考博文:
Android开发之旅:android架构
http://www.cnblogs.com/skynet/archive/2010/04/15/1712924.html
Android Dalvik虚拟机初识
首先,让我们来思考下面几个问题:
什么是Dalvik虚拟机?
Dalvik VM与JVM有什么区别?
Dalvik VM有什么新的特点?
Dalvik VM的架构是怎么样的?
首先,我得承认第一个问题问得很傻:什么是Dalvik虚拟机?没有人给出过一个明确的定义,但是,我们似乎可以从人们对Java虚拟机的描述中得到些信息。
Java虚拟机(JVM)是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。它有自己完善的硬件架构(如处理器、堆栈、寄存器等),还具有相应的指令系统。使用“Java虚拟机”程序就是为了支持与操作系统无关、在任何系统中都可以运行的程序。
因此,我们不妨对Dalvik虚拟机作出这样的描述:
Dalvik虚拟机是Android程序的虚拟机,是Android中Java程序的运行基础。其指令集基于寄存器架构,执行其特有的文件格式——dex字节码来完成对象生命周期管理、堆栈管理、线程管理、安全异常管理、垃圾回收等重要功能。它的核心内容是实现库(libdvm.so),大体由C语言实现。依赖于Linux内核的一部分功能——线程机制、内存管理机制,能高效使用内存,并在低速CPU上表现出的高性能。每一个Android应用在底层都会对应一个独立的Dalvik虚拟机实例,其代码在虚拟机的解释下得以执行。
与Dalvik虚拟机关系最密切的非JVM莫属,在Android源码readme文档中有这样一段话:Much of the code under this directory originally came from the Apache Harmony project, and as such contains the standard Apache header comment. Some of the code was written originally for the Android project…
Dalvik VM与Apache Harmony 项目关系源远流长,因此,与JVM关系自然就密切了。
然而:Dalvik VM ≠Java VM
dalvik基于寄存器,而JVM基于stack
Dalvik执行的是特有的DEX文件格式,而JVM运行的是*.class文件格式。
优势:1、在编译时提前优化代码而不是等到运行时
2、 虚拟机很小,使用的空间也小;被设计来满足可高效运行多种虚拟机实例。
3、常量池已被修改为只使用32位的索引,以 简化解释器
JVM的字节码主要是零地址形式的,概念上说JVM是基于栈的架构。Google Android平台上的应用程序的主要开发语言是Java,通过其中的Dalvik VM来运行Java程序。为了能正确实现语义,Dalvik VM的许多设计都考虑到与JVM的兼容性;但它却采用了基于寄存器的架构,其字节码主要是二地址/三地址的混合形式。
基于栈与基于寄存器的架构,谁更快?现在实际的处理器,大多都是基于寄存器的架构,从侧面反映出基于寄存器比基于栈的架构更与实际的处理器接近。但对于VM来说,源架构的求值栈或者寄存器都可能是用实际机器的内存来模拟的,所以性能特性与实际硬件又有不同。一般认为基于寄存器架构的Dalvik VM比基于栈架构JVM执行效率更高,原因是:虽然零地址指令更紧凑,但完成操作需要更多的load/store指令,也意味着更多的指令分派(instruction dispatch)次数与内存访问次数;访问内存是执行速度的一个重要瓶颈,二地址或三地址指令虽然每条指令占的空间较多,但总体来说可以用更少的指令完成操作,指令分派与内存访问次数都较少。
我们从下面的截图可以明了的看到与同一段Java代码对应的Java bytecode 与Dalvid bytecode的比较。
专有的DEX文件格式
一个应用中会定义很多类,
编译完成后即会有很多相应
的CLASS文件,CLASS文件
间会有不少冗余的信息。
dex字节码和标准Java的字节码(Class)在结构上的一个区别是dex字节码将多个文件整合成一个,这样,除了减少整体的文件尺寸,I/O操作,也提高了类的查找速度。
原来每个类文件中的常量池现在由DEX文件中一个常量池来管理。
DEX文件可以进行进一步优化。优化主要是针对以下几个方面:
1、调整所有字段的字节序(LITTLE_ENDIAN)和对齐结构中的没一个域
2、验证DEX文件中的所有类
3、对一些特定的类进行优化,对方法里的操作码进行优化
优化 优化后的文件大小会有所增加,应该是原DEX文件的1-4倍。
odex是为了在运行过程中进一步提高性能,对dex文件的进一步优化
一个应用,一个虚拟机实例,一个进程!!!
每一个Android应用都运行在一个Dalvik虚拟机实例里,而每一个虚拟机实例都是一个独立的进程空间。每个进程之间可以通信(IPC,Binder机制实现)。虚拟机的线程机制,内存分配和管理,Mutex等等都是依赖底层操作系统而实现的。
不同的应用在不同的进程空间里运行,当一个虚拟机关闭或意外中止时不会对其它 虚拟机造成影响,可以最大程度的保护应用的安全和独立运行。
Zygote是虚拟机实例的孵化器。AndroidRuntime.cpp中ZygoteInit.main()的执行会完成一个分裂,分裂出来的子进程继续初始化Java层的架构,这个分裂出来的进程就是system_server。每当系统要求执行一个Android应用程序,Zygote就会FORK出一个子进程来执行该应用程序。这样做的好处显而易见:Zygote进程是在系统启动时产生的,它会完成虚拟机的初始化,库的加载,预置类库的加载和初始化等等操作,而在系统需要一个新的虚拟机实例时,Zygote通过复制自身,最快速的提供个系统。另外,对于一些只读的系统库,所有虚拟机实例都和Zygote共享一块内存区域,大大节省了内存开销。