Java虚拟机(JVM)是一种用于计算机设备的规范,可用不同的方式(软件或硬件)加以实现,是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,它是 Java 最具吸引力的特性之一。
虚拟机是一种抽象化的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java 虚拟机有自己完善的硬体架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。Java 虚拟机屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 Java 虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。
定义
Java 虚拟机(Java Virtual Machine 简称 JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,它是 Java 最具吸引力的特性之一。
介绍
Java 虚拟机(JVM)一种用于计算机设备的规范,可用不同的方式(软件或硬件)加以实现。编译虚拟机的指令集与编译微处理器的指令集非常类似。Java 虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域。
Java 虚拟机(JVM)是可运行 Java 代码的假想计算机。只要根据 JVM 规格描述将解释器移植到特定的计算机上,就能保证经过编译的任何 Java 代码能够在该系统上运行。
Java 虚拟机是一个想象中的机器,在实际的计算机上通过软件模拟来实现。Java 虚拟机有自己想象中的硬件,如处理器、堆栈、寄存器等,还具有相应的指令系统。
Java 虚拟机规范定义了一个抽象的——而非实际的——机器或处理器。这个规范描述了一个指令集,一组寄存器,一个堆栈,一个“垃圾堆”,和一个方法区。一旦一个 Java 虚拟机在给定的平台上运行,任何 Java 程序(编译之后的程序,称作字节码)都能在这个平台上运行。Java 虚拟机(JVM)可以以一次一条指令的方式来解释字节码(把它映射到实际的处理器指令),或者字节码也可以由实际处理器中称作 just-in-time 的编译器进行进一步的编译。
特点
Java 语言的一个非常重要的特点就是与平台的无关性。而使用 Java 虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码。而引入 Java 语言虚拟机后,Java 语言在不同平台上运行时不需要重新编译。Java 语言使用模式 Java 虚拟机屏蔽了与具体平台相关的信息,使得 Java 语言编译程序只需生成在 Java 虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java 虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。
使用主体
Java 虚拟机是 Java 语言底层实现的基础。这有助于理解 Java 语言的一些性质,也有助于使用 Java 语言。对于要在特定平台上实现 Java 虚拟机的软件人员,Java 语言的编译器作者以及要用硬件芯片实现 Java 虚拟机的人来说,则必须深刻理解 Java 虚拟机的规范。另外,如果你想扩展 Java 语言,或是把其它语言编译成 Java 语言的字节码,你也需要深入地了解 Java 虚拟机。
安装方法
下载解压:
下载 j2sdk-1_4_2_05-linux-i586.bin 随便放到一个目录里,比如/tmp。
在终端里输入:sh j2sdk-1_4_2_05-linux-i586.bin 回车
之后会出现一堆软件说明,按回车 n 次直到问你 yes or no,当然回答 yes,输入 y,回车后开始解压缩。
完成之后,在/tmp 里就会出现一个名为 j2sdk1.4.2_05 的文件夹。
安装:
安装很简单:将 j2sdk1.4.2_05 文件夹复制到/usr 目录里。
设置环境变量:
只有设置好环境变量,系统才能调用 java 虚拟环境
打开/etc/profile 文件,在相关位置中加入:
export JAVA_HOME=/usr/j2sdk1.4.2_05
export PATH=/usr/j2sdk1.4.2_05/bin:$PATH
export CLASSPATH=/usr/j2sdk1.4.2_05/lib:/usr/j2sdk1.4.2_05/jre/lib:.:
保存
设置中文字体:
注意:下面涉及到的文件请先备份,以防万一!
进入/usr/j2sdk1.4.2_05/jre/lib/文件夹
删除里面所有带.zn 的文档,只留下 font.properties.zh 文档。
安装 simsun 字体如果不喜欢 simsun 可以不装。
编辑 font.properties.zh,将所有-tlc-song-medium-r-normal–*-%d-*-*-c-*-gbk-0 替换成:
-misc-simsun-medium-r-normal–*-%d-*-*-c-*-gbk-0(如果没装 simsun 字体,可以将-simsun-那里改成你喜欢的字体,前提是该字体在系统中存在)
之后在终端中转到目录/usr/j2sdk1.4.2_05/jre/bin/下
输入命令:
./ControlPanel 回车
数据类型
Java 虚拟机支持 Java 语言的基本数据类型有 8 种,注意 String 不是基本数据类型。
数据类型如下:
boolean://1 字节有符号整数的补码
byte://1 字节有符号整数的补码
short://2 字节有符号整数的补码
int://4 字节有符号整数的补码
long://8 字节有符号整数的补码
float://4 字节 IEEE754 单精度浮点数
double://8 字节 IEEE754 双精度浮点数
char://2 字节无符号 Unicode 字符
几乎所有的 Java 类型检查都是在编译时完成的。上面列出的原始数据类型的数据在 Java 执行时不需要用硬件标记。操作这些原始数据类型数据的字节码(指令)本身就已经指出了操作数的数据类型,例如 iadd、ladd、fadd 和 dadd 指令都是把两个数相加,其操作数类型别是 int、long、float 和 double。虚拟机没有给 boolean(布尔)类型设置单独的指令。boolean 型的数据是由 integer 指令,包括 integer 返回来处理的。boolean 型的数组则是用 byte 数组来处理的。虚拟机使用 IEEE754 格式的浮点数。不支持 IEEE 格式的较旧的计算机,在运行 Java 数值计算程序时,可能会非常慢。
虚拟机支持的其它数据类型包括:
object//对一个 Javaobject(对象)的 4 字节引用
returnAddress//4 字节,用于 jsr/ret/jsr-w/ret-w 指令
注:Java 数组被当作 object 处理。
虚拟机的规范对于 object 内部的结构没有任何特殊的要求。在 Sun 公司的实现中,对 object 的引用是一个句柄,其中包含一对指针:一个指针指向该 object 的方法表,另一个指向该 object 的数据。用 Java 虚拟机的字节码表示的程序应该遵守类型规定。Java 虚拟机的实现应拒绝执行违反了类型规定的字节码程序。Java 虚拟机由于字节码定义的限制似乎只能运行于 32 位地址空间的机器上。但是可以创建一个 Java 虚拟机,它自动地把字节码转换成 64 位的形式。从 Java 虚拟机支持的数据类型可以看出,Java 对数据类型的内部格式进行了严格规定,这样使得各种 Java 虚拟机的实现对数据的解释是相同的,从而保证了 Java 的与平台无关性和可移植性。
规格描述
JVM 的设计目标是提供一个基于抽象规格描述的计算机模型,为解释程序开发人员提范的任何系统上运行。JVM 对其实现的某些方面给出了具体的定义,特别是对 Java 可执行代码,即字节码(Bytecode)的格式给出了明确的规格。这一规格包括操作码和操作数的语法和数值、标识符的数值表示方式、以及 Java 类文件中的 Java 对象、常量缓冲池在 JVM 的存储映象。这些定义为 JVM 解释器开发人员提供了所需的信息和开发环境。Java 的设计者希望给开发人员以随心所欲使用 Java 的自由。
JVM 定义了控制 Java 代码解释执行和具体实现的五种规格,它们是:
*JVM 指令系统
*JVM 寄存器
*JVM 栈结构
*JVM 碎片回收堆
*JVM 存储区
体系结构
Java 虚拟机由五个部分组成:一组指令集、一组寄存器、一个栈、一个无用单元收集堆(Garbage-collected-heap)、一个方法区域。这五部分是 Java 虚拟机的逻辑成份,不依赖任何实现技术或组织方式,但它们的功能必须在真实机器上以某种方式实现。
指令集
Java 虚拟机支持大约 248 个字节码。每个字节码执行一种基本的 CPU 运算,例如,把一个整数加到寄存器,子程序转移等。Java 指令集相当于 Java 程序的汇编语言。
Java 指令集中的指令包含一个单字节的操作符,用于指定要执行的操作,还有 0 个或多个操作数,提供操作所需的参数或数据。许多指令没有操作数,仅由一个单字节的操作符构成。
虚拟机的内层循环的执行过程如下:
do{
取一个操作符字节;
根据操作符的值执行一个动作;
}while(程序未结束)
由于指令系统的简单性,使得虚拟机执行的过程十分简单,从而有利于提高执行的效率。指令中操作数的数量和大小是由操作符决定的。如果操作数比一个字节大,那么它存储的顺序是高位字节优先。例如,一个 16 位的参数存放时占用两个字节,其值为:
第一个字节*256+第二个字节字节码指令流一般只是字节对齐的。指令 tabltch 和 lookup 是例外,在这两条指令内部要求强制的 4 字节边界对齐。
运行过程
上面对虚拟机的各个部分进行了比较详细的说明,下面通过一个具体的例子来分析它的运行过程。
虚拟机通过调用某个指定类的方法 main 启动,传递给 main 一个字符串数组参数,使指定的类被装载,同时链接该类所使用的其它的类型,并且初始化它们。例如对于程序:
public class HelloApp {
public static void main(String[] args){
System.out.println(“Hello World!”);
for (int i = 0; i < args.length; i++ ) {
System.out.println(args);
}
}
}
编译后在命令行模式下键入:java HelloApp run virtual machine
将通过调用 HelloApp 的方法 main 来启动 java 虚拟机,传递给 main 一个包含三个字符串”run”、”virtual”、”machine”的数组。现在我们略述虚拟机在执行 HelloApp 时可能采取的步骤。
开始试图执行类 HelloApp 的 main 方法,发现该类并没有被装载,也就是说虚拟机当前不包含该类的二进制代表,于是虚拟机使用 ClassLoader 试图寻找这样的二进制代表。如果这个进程失败,则抛出一个异常。类被装载后同时在 main 方法被调用之前,必须对类 HelloApp 与其它类型进行链接然后初始化。链接包含三个阶段:检验,准备和解析。检验检查被装载的主类的符号和语义,准备则创建类或接口的静态域以及把这些域初始化为标准的默认值,解析负责检查主类对其它类或接口的符号引用,在这一步它是可选的。类的初始化是对类中声明的静态初始化函数和静态域的初始化构造方法的执行。一个类在初始化之前它的父类必须被初始化。整个过程如下: