简介
现在的Java系统是一个全方位的开发、运行平台,可以在任何计算环境中实现,我们根据其运行平台的特点将其分为高端和低端两种,高端是指所有非嵌入式系统的计算环境,SUN推出了J2SE和J2ME两种版式。在低端即嵌入式系统,SUN推出了J2ME(Java 2 Platform Micro Edition)本文重点介绍J2ME。J2ME是SUN为了适应市场上日渐壮大的信息家电设备,如Mobile Phone,PDA等设备而推出的微型JAVA技术,主要用于开发电子产品,例如,移动电话、数字机顶盒、汽车导航系统等2。
J2ME体系结构如图所示,其中profile,configuration和jvm是J2ME的组成部分。 J2ME是一个简化的JAVA,并针对各种不同的底端设备而设计的,为了满足各种环境J2ME设计了pro-file configuration规范。 configuration是一个语言特性和核心类的配置规范,针对不同的环境(内存、显示、网络连接和处理能力)给定不同的配置。也就是定义了该环境的JAVA语言子集和核心类库,虚拟机开发者遵照此配置来开发虚拟机,最著名的两个配置是连接设备配置(CDC)和连结有限设备配置(CLDC)。另一方面, profile是针对相同的configuration的不同特性而开发的扩展类库,它提供给开发者在特定类型设备上开发的类库。如移动信息设备子集MIDP(Mobile Information De-vice Profile)就是综合考虑了移动设备的屏幕和内存限制,定义了一些用户接口,事件处理,存储,网络等APIs。
根据具体的configuration,形成了具体的虚拟机需求,从而要求人们针对其进行相应的开发,如CDC因为其设备是较高端的,有丰富的资源,因此CDC使用了与J2SE一样的JVM,而CLDC由于其设备资源受到限制,所以Sun专门开发了一个新的虚拟机KVM(K VirtualMachine)。当然,只要符合CLDC规范,我们也可以开发自己的虚拟机。如IBM开发的J9、visual-Age,微软的MSJVM等,都符合JAVA虚拟机的规范,但还有各自不同的特点,具体内容请读者参考相关资料。
机制JAVA的基本原理JAVA的目标和思想是“WRITE ONCE RUN ANYWHERE,”这是通过JAVA虚拟机机制来实现的。 JAVA虚拟机是一个纯软件的计算环境,建立在各种具体的硬件和操作系统上,实现了一个软指令计算环境和垃圾回收管理内存,并在其上建立了类概念,编译后的JAVA字节码可在其上运行。
Java以bytecode为中间码,以对象为管理单位,以栈形式管理内存,使用翻译的运行方式执行目标程序,图是JAVA应用的运行流程。
程序开发平台a) JAVA语言规范
嵌入式Java虚拟机的开发平台首先定义了一套语言规范,它是Java语言规范的子集。
1) Java语言规范使用上下文无关文法,以C和C++为基础定义了自己的词法结构、数据类型、数值和变量,其中,类型被细分为基本类型和扩展类型。基本类型在所有的机器上和所有的执行中被定义成一样的。包括整型、单精度的浮点型、双精度的浮点型、布尔型和Unicode字符型。扩展类型包括类类型、接口类型和数组类型。通过动态地创建类或数组的对象实例来实现扩展类型。数据类型支持类型转换。
2) Java是面向对象的语言,以包的形式管理项目,以类为编译单元。 Java类由域成员、方法成员和属性成员组成,并包含异常处理,与C++不同, Java语言既不把声明“ header”从类的实现中分开,也不把类型和类层次从类的实现中分开,并且把数组作为动态创建的对象,支持数组的数组,而不是多维数组。
3) Java语言规范定义了一个基本类型,基本类库包含三个包: java. lang. java. io和java. util。其中java lang包定义了核心运行时类、核心数据类型类和帮助类: java. io包中则定义了输入输出类;而java. util包则定义了收集类和其它类(时间、日期和随机数类等)。各个不同的嵌入式Java虚拟机在以上基本类库的基础上通过添加类来实现自己的特殊功能。
b)开发环境
对于现代的程序员,开发工具起着越来越重要的作用。使用嵌入式JAVA虚拟机进行程序开发,像开发PC机上的JAVA程序一样,可以有很多种选择。你可以直接使用文本编辑器写程序,然后使用JDK进行编译调试,也可以使用各种IDE进行编程,有很多种IDE可选,如Visual J++,Borland JBuilder、VisualAgeJava等。但与PC与JAVA程序所不同的是,我们要针对嵌入式系统安装不同的是SDK,并使用不同的程序发布机制。另外,由于嵌入式系统的资源有限性,我们还需在发布之前完成程序的校验工作。
c)类校验
类校验是JAVA虚拟机为了提高安全性而采用的一种方法,用于检验每个CLASS文件是否都满足必要的约束,并且也可以提高解释期的性能。类校验的基本内容有: 确认该文件是不是具Java class文件的基本格式。 进行一些附加检验,如:确认final类不被子类化, final方法不被覆盖;确认每个类(除object)有一个超类等等。 对类中的每个方法进行检验,确认参数的合法性,局部变量访问的合法性等问题。
d)程序的发布
应用程序编译后运用打包工具,如使用专门的打包工具或者是IDE中包含的打包工具,或者由开发人员自己开发的打包工具,把预验证后的类文件、资源文件、清单文件打包,如果程序中还要用到其它类型的文件,也需要添加到该包文件中,然后就可以发布了。比如要发布JAVA程序,可以使用jar工具(来自J2SE)来打包,并且还需要编写一描述文件(JAD), JAD的要求同清单文件一致。
运行平台a)程序管理
JAVA程序是以类文件或发布包的形式进行分发的,对PC系统而言,因为有文件系统,管理是比较统一的,但对嵌入式系统而言,实现平台各异,不一定都有OS或文件系统,因此,程序管理的实现将因系统而异,但其内容应基本一致,包括程序的安装、查看、删除和启动等。
b)虚拟机的实现平台
目前嵌入式JAVA虚拟机的实现平台具有多种方式。它可以直接嵌入到裸机上,也可以加载于其他嵌入式操作系统之上,成为一台抽象的计算机。如它既可以运行在嵌入式linux平台上,也可以运行在windowsCE平台。它的平台无关性给它带来了巨大的发展前景。更值得一提的是,目前sun公司已开发出了Java芯片,就其功能而言也不异于JAVA虚拟机,这种芯片可以说是Java虚拟机的硬件实现。
c)Class文件格式
JVM对其实现的某些方面给出了具体的定义,特别是对Java可执行代码,即字节码(Bytecode)的格式给出了明确的规格。这一规格包括操作码和操作数的语法和数值、标识符的数值表示方式、以及Java类文件中的Java对象、常量缓冲池在JVM的存储映象。这些定义为JVM解释器开发人员提供了所需的信息和开发环境。 Java的设计者希望给开发人员以随心所欲使用Java的自由。下面介绍一下Class文件格式:
我们在编写JAVA程序时,编译器将为每一个用户定义的JAVA Class都成一个独立的类文件,因此我们把JAVA Class的构架名称称为ClassFile,CLASSFILE是一个有结构的字节码编译后中间文件,按类的结构进行组织,主要包括JAVA类文件识别码、版本号、常数池记录长度、常数池、类访问标志、本类类名索引、超类索引、该类所实现的接口记录长度、接口记录表、该类的域成员数目、域成员组、该类的方法数目、方法表、该类的属性数目、属性组(属性用于记录类静态成员变量的初始值或方法的代码或方法的异常处理类型等具体内容)等。篇幅有限,这里我们对ClassFile中各标记代表的具体内容不作介绍。
d)虚拟机的内存结构
JAVA虚拟机是一个面向对象的机器,所以,以类为核心结合线程机制进行管理。在整个系统中,通过类的加载机制完成从类文件到运行时类实体结构的转换,而在类实体结构中包含了该类所定义的各种实体的内容,包括类成员变量定义表、类方法定义、类的常数池(相当于符号表)以及该类的静态成员变量表,这些内容构成了类定义的全部,而该类的实例表则是所有该类的实例对象的索引,类的实例对象的结构都是通过类的成员变量定义表来表达的,对象本身的定义只是一个byte数组及一个指向该类实体结构的索引。
当VM建立一个Java thread的同时,会在Java heap中一同建立起一个专属于该thread的excution stack,而存储在这个exe-cution stack中的是一种称作frame结构的数据结构。 JavaVM为方法的每一次执行建立一个frame,在frame中存储方法的局部变量和参数,并为该函数的执行建立一个局部的操作数栈,该方法函数的所有操作都针对该局部操作数栈。而不论该method是正常或不正常的结束执行时,VM都会把在execution stack中与之对应的frame给释放掉。Java具备垃圾回收的内存管理机制,这主要是针对对象和数组对象来说的,对象和数组是在回收堆中进行分配的,当该对象无用时,系统将释放对象回收该内存。虚拟机内存总体的结构图如图所示。
e)指令系统
Java虚拟机是一个虚拟的计算机,像其他计算机一样有自己的寄存器和指令集。为了提高虚拟机指令的执行效率, JAVA VM只设置了4个最为常用的寄存器。它们是:
pc程序计数器;
optop操作数栈顶指针;
frame当前执行环境指针;
vars指向当前执行环境中第一个局部变量的指针。
所有寄存器均为32位。 pc用于记录程序的执行。 optop,frame和vars用于记录指向Java栈区的指针。JAVA VM指令系统同其他计算机的指令系统极其相似, Ja-va指令也是由操作码和操作数两部分组成。操作码为8位二进制数, Java的8位操作码的长度使得JVM最多有256种指令,目前已使用了160多种操作码。操作数紧随在操作码的后面,其长度根据需要而不同。当长度大于8位时,操作数被分为两个以上字节存放。
JVM采用了“ big endian”的编码方式来处理这种情况,即高位bits存放在低字节中。JAVA虚拟机规范定义了许多指令,按功能可分以下类别:装载和存储指令、运算指令、类型转换指令、对象创建和操纵、操作数栈管理指令、控制转移指令、方法调用和返回指令、抛出和处理异常的指令、实现finally的指令、同步指令等。而指令的操作数则主要包括以下四种:
当前方法的FRAME框架的局部变量的索引值;
当前类的常数池的索引值,通过该索引值来定位操作数的具体位置;
方法域的相对当前指令的偏移量(如控制转移指令、异常等);
操作数栈。
对于对象或数组变量,其值均指对象在堆内存中的指针,为引用变量,需要通过计算取得值,而对于其他变量,可看作是实数变量,包括操作数种的实体变量、当前框架的实体变量和指令中的操作数。
f)运行流程
运行JVM字节码的工作是由解释器来完成的。解释执行过程分三部进行:代码的装入、代码的校验和代码的执行。装入代码的工作由“类装载器”(class loader)完成。类装载器负责装入运行一个程序需要的所有代码,这也包括程序代码中的类所继承的类和被其调用的类。当类装载器装入一个类时,该类被放在自己的名字空间中。除了通过符号引用自己名字空间以外的类,类之间没有其他办法可以影响其他类。在本台计算机上的所有类都在同一地址空间内,而所有从外部引进的类,都有一个自己独立的名字空间。这使得本地类通过共享相同的名字空间获得较高的运行效率,同时又保证它们与从外部引进的类不会相互影响。当装入了运行程序需要的所有类后,解释器便可确定整个可执行程序的内存布局。解释器为符号引用同特定的地址空间建立对应关系及查询表。通过在这一阶段确定代码的内存布局, Java很好地解决了由超类改变而使子类崩馈的问题,同时也防止了代码对地址的非法访问。随后,被装入的代码由字节码校验器进行检查。校验器可发现操作数栈溢出,非法数据类型转化等多种错误。通过校验后,代码便开始执行了。