jvm

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值