zoukankan      html  css  js  c++  java
  • Java虚拟机定义

    虚拟机是一种抽象化的计算机。通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机有自己完好的硬体架构。如处理器堆栈寄存器等。还具有对应的指令系统。JVM屏蔽了与详细操作系统平台相关的信息,使得Java程序仅仅需生成在Java虚拟机上运行的目标代码(字节码),就能够在多种平台上不加改动地运行。[1] 
    中文名
    java虚拟机
    外文名
    Java Virtual Machine

    1定义编辑

    Java虚拟机(Java Virtual Machine 简称JVM)是运行全部Java程序的抽象计算机。是Java语言的运行环境。它是Java 最具吸引力的特性之中的一个。

    2简单介绍编辑

    Java虚拟机(JVM)一种用于计算机设备的规范,可用不同的方式(软件或硬件)加以实现。编译虚拟机指令集与编译微处理器指令集很相似。Java虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域。
    Java虚拟机(JVM)是可运行Java代码的假想计算机。仅仅要依据JVM规格描写叙述将解释器移植到特定的计算机上,就能保证经过编译的不论什么Java代码能够在该系统上运行。
    Java虚拟机是一个想象中的机器,在实际的计算机上通过软件模拟来实现。Java虚拟机有自己想象中的硬件,如处理器、堆栈、寄存器等。还具有对应的指令系统。

    3特点编辑

    Java语言的一个很重要的特点就是与平台的无关性。

    而使用Java虚拟机是实现这一特点的关键。

    一般的高级语言假设要在不同的平台上运行,至少须要编译成不同的目标代码

    而引入Java语言虚拟机后,Java语言在不同平台上运行时不须要又一次编译。Java语言使用模式Java虚拟机屏蔽了与详细平台相关的信息。使得Java语言编译程序仅仅需生成在Java虚拟机上运行的目标代码字节码)。就能够在多种平台上不加改动地运行。Java虚拟机在运行字节码时,把字节码解释成详细平台上的机器指令运行。

    4使用主体编辑

    Java虚拟机是Java语言底层实现的基础。这有助于理解Java语言的一些性质。也有助于使用Java语言。对于要在特定平台上实现Java虚拟机软件人员。Java语言的编译器作者以及要用硬件芯片实现Java虚拟机的人来说,则必须深刻理解Java虚拟机的规范。另外。假设你想扩展Java语言。或是把其他语言编译成Java语言的字节码,你也须要深入地了解Java虚拟机。

    5安装方法编辑

    java虚拟机安装方法

    java虚拟机安装方法[2]

    下载解压
    下载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的文件夹。
    安装
    安装很easy:将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回车

    6数据类型编辑

    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的与平台无关性和可移植性

    7规格描写叙述编辑

    JVM的设计目标是提供一个基于抽象规格描写叙述的计算机模型,为解释程序开发者提范的不论什么系统上运行。

    JVM对事实上现的某些方面给出了详细的定义。特别是对Java可运行代码,即字节码(Bytecode)的格式给出了明白的规格。这一规格包括操作码操作数的语法和数值、标识符的数值表示方式、以及Java类文件里的Java对象、常量缓冲池在JVM的存储映象。这些定义为JVM解释器开发者提供了所需的信息和开发环境。Java的设计者希望给开发者以随心所欲使用Java的自由。

    JVM定义了控制Java代码解释运行和详细实现的五种规格,它们是:
    *JVM栈结构
    *JVM碎片回收堆
    *JVM存储区

    JVM指令系统

    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种指令,java1.6及以上版本号已使用了160多种操作码。

    JVM寄存器

    全部的CPU均包括用于保存系统状态和处理器所需信息的寄存器组。假设虚拟机定义较多的寄存器。便能够从中得到很多其他的信息而不必对栈或内存进行訪问。这有利于提高运行速度。然而,假设虚拟机中的寄存器比实际CPU的寄存器多。在实现虚拟机时就会占用处理器大量的时间来用常规存储器模拟寄存器。这反而会减少虚拟机的效率。针对这样的情况,JVM仅仅设置了4个最为经常使用的寄存器。它们是:pc程序计数器optop操作数栈顶指针frame当前运行环境指针 vars指向当前运行环境中第一个局部变量的指针 全部寄存器均为32位。pc用于记录程序的运行。

    optop,frame和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++原程序编译后的80x86的机器码(二进制程序文件)。JVM虚拟机相当于80x86计算机系统,Java解释器相当于80x86CPU。在80x86CPU上运行的是机器码,在Java解释器上运行的是Java字节码。

    Java解释器相当于运行Java字节码的“CPU”,但该“CPU”不是通过硬件实现的,而是用软件实现的。Java解释器实际上就是特定的平台下的一个应用程序。仅仅要实现了特定平台下的解释器程序,Java字节码就能通过解释器程序在该平台下运行,这是Java跨平台的根本。

    当前,并非在全部的平台下都有对应Java解释器程序,这也是Java并不能在全部的平台下都能运行的原因。它仅仅能在已实现了Java解释器程序的平台下运行。

    8体系结构编辑

    Java虚拟机由五个部分组成:一组指令集、一组寄存器、一个、一个无用单元收集堆(Garbage-collected-heap)、一个方法区域。

    这五部分是Java虚拟机的逻辑成份。不依赖不论什么实现技术或组织方式,但它们的功能必须在真实机器上以某种方式实现。

    Java指令集

    Java虚拟机支持大约248个字节码。每一个字节码运行一种基本的CPU运算。比如。把一个整数加到寄存器子程序转移等。Java指令集相当于Java程序的汇编语言。
    Java指令集中的指令包括一个单字节的操作符,用于指定要运行的操作,还有0个或多个操作数。提供操作所需的參数或数据。

    很多指令没有操作数,仅由一个单字节的操作符构成。

    虚拟机的内层循环的运行步骤例如以下:
    do{
    取一个操作符字节;
    依据操作符的值运行一个动作;
    }while(程序未结束)
    因为指令系统的简单性,使得虚拟机运行的过程十分简单,从而有利于提高运行的效率。

    指令中操作数的数量和大小是由操作符决定的。假设操作数比一个字节大,那么它存储的顺序是高位字节优先。比如,一个16位的參数存放时占用两个字节,其值为:

    第一个字节*256+第二个字节字节码指令流一般仅仅是字节对齐的。指令tabltch和lookup是例外,在这两条指令内部要求强制的4字节边界对齐

    寄存器

    Java虚拟机寄存器用于保存机器的运行状态,与微处理器中的某些专用寄存器相似。
    Java虚拟机寄存器有四种:
    pc:Java程序计数器
    optop:指向操作数栈顶端的指针
    frame:指向当前运行方法的运行环境的指针

    vars:指向当前运行方法的局部变量区第一个变量的指针

    Java虚拟机的栈有三个区域:局部变量区、运行环境区、操作数区。

    局部变量区 每一个Java方法使用一个固定大小的局部变量集。它们依照与vars寄存器的字偏移量来寻址。

    局部变量都是32位的。

    长整数和双精度浮点数占领了两个局部变量的空间,却依照第一个局部变量的索引来寻址。(比如,一个具有索引n的局部变量,假设是一个双精度浮点数,那么它实际占领了索引n和n+1所代表的存储空间。)虚拟机规范并不要求在局部变量中的64位的值是64位对齐的。虚拟机提供了把局部变量中的值装载到操作数栈的指令。也提供了把操作数栈中的值写入局部变量的指令。

    ⑵运行环境区 在运行环境中包括的信息用于动态链接,正常的方法返回以及异常传播
    ·动态链接
    运行环境包括对指向当前类和当前方法的解释器符号表指针,用于支持方法代码的动态链接

    方法的class文件代码在引用要调用的方法和要訪问的变量时使用符号。动态链接把符号形式的方法调用翻译成实际方法调用,装载必要的类以解释还未定义的符号。并把变量訪问翻译成与这些变量运行时的存储结构对应的偏移地址动态链接方法和变量使得方法中使用的其他类的变化不会影响到本程序的代码。

    ·正常的方法返回
    假设当前方法正常地结束了,在运行了一条具有正确类型的返回指令时。调用的方法会得到一个返回值。运行环境在正常返回的情况下用于恢复调用者的寄存器,并把调用者的程序计数器添加一个恰当的数值。以跳过已运行过的方法调用指令。然后在调用者的运行环境中继续运行下去。
    ·异常和错误传播
    异常情况在Java中被称作Error(错误)或Exception(异常),是Throwable类的子类,在程序中的原因是:①动态链接错,如无法找到所需的class文件。②运行时错,如对一个空指针的引用
    ·程序使用了throw语句。
    当异常发生时,Java虚拟机採取例如以下措施:
    ·检查与当前方法相联系的catch子句表。每一个catch子句包括其有效指令范围,能够处理的异常类型,以及处理异常的代码块地址。

    ·与异常相匹配的catch子句应该符合以下的条件:造成异常的指令在其指令范围之内,发生的异常类型是其能处理的异常类型的子类型。

    假设找到了匹配的catch子句,那么系统转移到指定的异常处理块处运行;假设没有找到异常处理块,反复寻找匹配的catch子句的过程,直到当前方法的全部嵌套的catch子句都被检查过。

    ·因为虚拟机从第一个匹配的catch子句处继续运行,所以catch子句表中的顺序是很重要的。因为Java代码是结构化的,因此总能够把某个方法的全部的异常处理器都按序排列到一个表中,对随意可能的程序计数器的值,都能够用线性的顺序找到合适的异常处理块。以处理在该程序计数器值下发生的异常情况。

    ·假设找不到匹配的catch子句。那么当前方法得到一个"未截获异常"的结果并返回到当前方法的调用者,好像异常刚刚在其调用者中发生一样。

    假设在调用者中仍然没有找到对应的异常处理块,那么这样的错误传播将被继续下去。假设错误被传播到最顶层,那么系统将调用一个缺省的异常处理块。

    操作数栈区机器指令仅仅从操作数栈中取操作数,对它们进行操作。并把结果返回到栈中。

    选择栈结构的原因是:在仅仅有少量寄存器或非通用寄存器的机器(如Intel486)上,也能够高效地模拟虚拟机的行为。操作数栈是32位的。它用于给方法传递參数,并从方法接收结果,也用于支持操作的參数。并保存操作的结果。比如,iadd指令将两个整数相加。相加的两个整数应该是操作数栈顶的两个字。这两个字是由先前的指令压进堆栈的。这两个整数将从堆栈弹出、相加。并把结果压回到操作数栈中。

    每一个原始数据类型都有专门的指令对它们进行必须的操作。每一个操作数在栈中须要一个存储位置,除了long和double型,它们须要两个位置。操作数仅仅能被适用于其类型的操作符所操作。比如,压入两个int类型的数,假设把它们当作是一个long类型的数则是非法的。在Sun的虚拟机实现中,这个限制由字节码验证器强制实行。

    可是。有少数操作(操作符dupe和swap),用于对运行时数据区进行操作时是不考虑类型的。

    无用单元收集堆

    Java的堆是一个运行时数据区。类的实例(对象)从中分配空间。Java语言具有无用单元收集能力:它不给程序猿显式释放对象的能力。Java不规定详细使用的无用单元收集算法,能够依据系统的需求使用各种各样的算法。

    方法区

    方法区与传统语言中的编译后代码或是Unix进程中的正文段相似。

    它保存方法代码(编译后的java代码)和符号表

    在当前的Java实现中。方法代码不包括在无用单元收集堆中。但计划在将来的版本号中实现。每一个类文件包括了一个Java类或一个Java界面的编译后的代码。能够说类文件是Java语言的运行代码文件。

    为了保证类文件的平台无关性,Java虚拟机规范中对类文件的格式也作了详细的说明。其详细细节请參考Sun公司的Java虚拟机规范。

    9运行过程编辑

    上面对虚拟机的各个部分进行了比較详细的说明。以下通过一个详细的样例来分析它的运行过程。
    虚拟机通过调用某个指定类的方法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与其他类型进行链接然后初始化。链接包括三个阶段:检验,准备和解析。

    检验检查被装载的主类的符号和语义。准备则创建类或接口的静态域以及把这些域初始化为标准的默认值,解析负责检查主类对其他类或接口的符号引用,在这一步它是可选的。类的初始化是对类中声明的静态初始化函数和静态域的初始化构造方法的运行。一个类在初始化之前它的父类必须被初始化。

    整个步骤例如以下:

    10參数说明编辑

    一、运行class文件
    运行带main方法的class文件,Java虚拟机[3] 命令參数行为:
    java <CLASS文件名称>
    注意:CLASS文件名称不要带文件后缀.class
    比如:
    java Test
    假设运行的class文件是带包的。即在类文件里使用了:
    package <。包名>
    那应该在包的基路径下运行。Java虚拟机命令行參数
    java <;包名>.CLASS文件名称
    比如:
    PackageTest.java中,其包名为:com.ee2ee.test,对应的语句为:
    package com.ee2ee.test;
    PackageTest.java及编译后的class文件PackageTest.class的存放文件夹例如以下:
    classes
    |__com
    |__ee2ee
    |__test
    |__PackageTest.java
    |__PackageTest.class
    要运行PackageTest.class,应在classes文件夹下运行:
    java com.ee2ee.test.PackageTest
    二、运行jar文件里的class
    原理和运行class文件一样。仅仅需加上參数-cp <jar文件名称>;就可以。

    比如:运行test.jar中的类com.ee2ee.test.PackageTest,命令行例如以下:
    java -cp test.jar com.ee2ee.test.PackageTest
    三、显示JDK版本号信息
    当一台机器上有多个jdk版本号时。须要知道当前使用的是那个版本号的jdk,使用參数-version就可以知道其版本号,命令行为:
    java -version
    四、添加虚拟机能够使用的最大内存
    Java虚拟机可使用的最大内存是有限制的,缺省值通常为64MB或128MB。

    假设一个应用程序为了提高性能而把数据载入内存中而占用较大的内存,比方超过了默认的最大值128MB,须要加大java虚拟机可使用的最大内存,否则会出现Out of Memory(系统内存不足)的异常。启动java时。须要使用例如以下两个參数:
    -Xms java虚拟机初始化时使用的内存大小
    -Xmx java虚拟机能够使用的最大内存
    以上两个命令行參数中设置的size。能够带单位,比如:256m表示256MB
    举例说明:
    java -Xms128m -Xmx256m ...
    表示Java虚拟机初始化时使用的内存为128MB,可使用的最大内存为256MB。
    对于tomcat,能够改动其脚本catalina. sh(Unix平台)或catalina.bat(Windows平台),设置变量JAVA_OPTS就可以,比如:
    JAVA_OPTS='-Xms128m -Xmx256m'

    Java(甲骨文公司)

    Java平台
     Java 编程语言 JVM Java ME Java SE
     Java EE Java Card  
     
    升阳电脑主要技术
     Squawk JDK OpenJDK Java Virtual Machine
     JavaFX   
     
    平台技术
     Applet Servlets MIDlets JSP
     Web Start (jnlp)   
     
    基本的第三方技术
     JRockit GNU Classpath Kaffe TopLink
     Apache Harmony Struts Spring Framework Hibernate
     JBoss Tapestry Jazelle 
     
    历史
     历史 批评 Java Community Process 升阳电脑
     Free Java implementations   
     
    主要编程语言
     Tcl/Java Jython JRuby BeanShell
     Clojure Groovy Rhino Scala
     Processing   
     
    Java会议
     JavaOne   
     
    參考资料
  • 相关阅读:
    显示图案
    圆的面积和周长
    Python基础--list列表删除元素
    Python基础--列表添加元素
    Python基础--列表创建访问删除
    Python基础--序列简介
    一个网页通用的测试用例(转载)
    测试计划与测试方案的区别
    判断一棵树是否是二叉平衡树
    判断丑数与寻找丑数
  • 原文地址:https://www.cnblogs.com/claireyuancy/p/6751542.html
Copyright © 2011-2022 走看看