Java虚拟机(JVM)是可运行Java代码的假想计算机只要根据JVM规格描述将解释器移植到特定的计算机上就能保证经过编译的任何Java代码能够在该系统上运行本文首先简要介绍从Java文件的编译到最终执行的过程随后对JVM规格描述作一说明
一Java源文件的编译下载解释和执行
Java应用程序的开发周期包括编译下载解释和执行几个部分Java编译程序将Java源程序翻译为JVM可执行代码?字节码这一编译过程同C/C++的编译有些不同当C编译器编译生成一个对象的代码时该代码是为在某一特定硬件平台运行而产生的因此在编译过程中编译程序通过查表将所有对符号的引用转换为特定的内存偏移量以保证程序运行Java编译器却不将对变量和方法的引用编译为数值引用也不确定程序执行过程中的内存布局而是将这些符号引用信息保留在字节码中由解释器在运行过程中创立内存布局然后再通过查表来确定一个方法所在的地址这样就有效的保证了Java的可移植性和安全性
运行JVM字节码的工作是由解释器来完成的解释执行过程分三部进行代码的装入代码的校验和代码的执行装入代码的工作由类装载器(class loader)完成类装载器负责装入运行一个程序需要的所有代码这也包括程序代码中的类所继承的类和被其调用的类当类装载器装入一个类时该类被放在自己的名字空间中除了通过符号引用自己名字空间以外的类类之间没有其他办法可以影响其他类在本台计算机上的所有类都在同一地址空间内而所有从外部引进的类都有一个自己独立的名字空间这使得本地类通过共享相同的名字空间获得较高的运行效率同时又保证它们与从外部引进的类不会相互影响当装入了运行程序需要的所有类后解释器便可确定整个可执行程序的内存布局解释器为符号引用同特定的地址空间建立对应关系及查询表通过在这一阶段确定代码的内存布局Java很好地解决了由超类改变而使子类崩溃的问题同时也防止了代码对地址的非法访问
随后被装入的代码由字节码校验器进行检查校验器可发现操作数栈溢出非法数据类型转化等多种错误通过校验后代码便开始执行了
Java字节码的执行有两种方式
即时编译方式解释器先将字节码编译成机器码然后再执行该机器码
解释执行方式解释器通过每次解释并执行一小段代码来完成Java字节码程 序的所有操作
通常采用的是第二种方法由于JVM规格描述具有足够的灵活性这使得将字节码翻译为机器代码的工作
具有较高的效率对于那些对运行速度要求较高的应用程序解释器可将Java字节码即时编译为机器码从而很好地保证了Java代码的可移植性和高性能
二JVM规格描述
JVM的设计目标是提供一个基于抽象规格描述的计算机模型为解释程序开发人员提很好的灵活性同时也确保Java代码可在符合该规范的任何系统上运行JVM对其实现的某些方面给出了具体的定义特别是对Java可执行代码即字节码(Bytecode)的格式给出了明确的规格这一规格包括操作码和操作数的语法和数值标识符的数值表示方式以及Java类文件中的Java对象常量缓沖池在JVM的存储映象这些定义为JVM解释器开发人员提供了所需的信息和开发环境Java的设计者希望给开发人员以随心所欲使用Java的自由
JVM定义了控制Java代码解释执行和具体实现的五种规格它们是
JVM指令系统
JVM寄存器
JVM栈结构
JVM碎片回收堆
JVM存储区
JVM指令系统
JVM指令系统同其他计算机的指令系统极其相似Java指令也是由 操作码和操作数两部分组成操作码为位二进制数操作数进紧随在操作码的后面其长度根据需要而不同操作码用于指定一条指令操作的性质(在这里我们采用汇编符号的形式进行说明)如iload表示从存储器中装入一个整数anewarray表示为一个新数组分配空间iand表示两个整数的与ret用于流程控制表示从对某一方法的调用中返回当长度大于位时操作数被分为两个以上字节存放JVM采用了big endian的编码方式来处理这种情况即高位bits存放在低字节中这同 Motorola及其他的RISC CPU采用的编码方式是一致的而与Intel采用的little endian 的编码方式即低位bits存放在低位字节的方法不同
Java指令系统是以Java语言的实现为目的设计的其中包含了用于调用方法和监视多先程系统的指令Java的位操作码的长度使得JVM最多有种指令目前已使用了多种操作码
JVM指令系统
所有的CPU均包含用于保存系统状态和处理器所需信息的寄存器组如果虚拟机定义较多的寄存器便可以从中得到更多的信息而不必对栈或内存进行访问这有利于提高运行速度然而如果虚拟机中的寄存器比实际CPU的寄存器多在实现虚拟机时就会占用处理器大量的时间来用常规存储器模拟寄存器这反而会降低虚拟机的效率针对这种情况JVM只设置了个最为常用的寄存器它们是
pc程序计数器
optop操作数栈顶指针
frame当前执行环境指针
vars指向当前执行环境中第一个局部变量的指针
所有寄存器均为位pc用于记录程序的执行optopframe和vars用于记录指向Java栈区的指针
JVM栈结构
作为基于栈结构的计算机Java栈是JVM存储信息的主要方法当JVM得到一个Java字节码应用程序后便为该代码中一个类的每一个方法创建一个栈框架以保存该方法的状态信息每个栈框架包括以下三类信息
局部变量
执行环境
操作数栈
局部变量用于存储一个类的方法中所用到的局部变量vars寄存器指向该变量表中的第一个局部变量
执行环境用于保存解释器对Java字节码进行解释过程中所需的信息它们是上次调用的方法局部变量指针和操作数栈的栈顶和栈底指针执行环境是一个执行一个方法的控制中心例如如果解释器要执行iadd(整数加法)首先要从frame寄存器中找到当前执行环境而后便从执行环境中找到操作数栈从栈顶弹出两个整数进行加法运算最后将结果压入栈顶
操作数栈用于存储运算所需操作数及运算的结果
JVM碎片回收堆
Java类的实例所需的存储空间是在堆上分配的解释器具体承担为类实例分配空间的工作解释器在为一个实例分配完存储空间后便开始记录对该实例所占用的内存区域的使用一旦对象使用完毕便将其回收到堆中
在Java语言中除了new语句外没有其他方法为一对象申请和释放内存对内存进行释放和回收的工作是由Java运行系统承担的这允许Java运行系统的设计者自己决定碎片回收的方法在SUN公司开发的Java解释器和Hot Java环境中碎片回收用后台线程的方式来执行这不但为运行系统提供了良好的性能而且使程序设计人员摆脱了自己控制内存使用的风险
JVM存储区
JVM有两类存储区常量缓沖池和方法区常量缓沖池用于存储类名称方法和字段名称以及串常量方法区则用于存储Java方法的字节码对于这两种存储区域具体实现方式在JVM规格中没有明确规定这使得Java应用程序的存储布局必须在运行过程中确定依赖于具体平台的实现方式
JVM是为Java字节码定义的一种独立于具体平台的规格描述是Java平台独立性的基础目前的JVM还存在一些限制和不足有待于进一步的完善但无论如何JVM的思想是成功的
对比分析如果把Java原程序想象成我们的C++原程序Java原程序编译后生成的字节码就相当于C++原程序编译后的x的机器码(二进制程序文件)JVM虚拟机相当于x计算机系统Java解释器相当于xCPU在xCPU上运行的是机器码在Java解释器上运行的是Java字节码
Java解释器相当于运行Java字节码的CPU但该CPU不是通过硬件实现的而是用软件实现的Java解释器实际上就是特定的平台下的一个应用程序只要实现了特定平台下的解释器程序Java字节码就能通过解释器程序在该平台下运行这是Java跨平台的根本当前并不是在所有的平台下都有相应Java解释器程序这也是Java并不能在所有的平台下都能运行的原因它只能在已实现了Java解释器程序的平台下运行