欢迎来到学术参考网

Java虚拟机分析

发布时间:2016-03-31 16:17

  1 Java虚拟机

  Java不仅是一种跨平台的语言,而且是一种新的网络计算平台.该平台包括许多相关的技术,如符合开放接口标准的各种API优化技术等.Jjva技术使同一种应用可以运行在不同的平台上.Java平台可分为两部分,即Java虚拟机(Javavirtualma­chine,JVM)和JavaAPI类库

  在Java平台的结构中,JVM处在核心的位置.它的下方是移植接口.移植接口由两部分组成,其中依赖于平台的部分称为适配器.JVM通过移植接口在具体的平台和操作系统上实现.在JVM的上方是Java的基本类库和API.利用JavaAPI编写的应用程序(application)和小程序(Javaapplet)可在任何Java平台上运行而无需考虑底层平台,从而实现了Java的平台无关性.Java程序的执行过程是:首先,源程序(.java)经过编译器编译后生成由字节码组成的类文件(.class).然后由类装载器装入来自类文件的字节码,经过检验器安全验证后,由JVM读取字节码,转换为特定平台的指令,并在相应的CPU中运行.

  JVM是Java程序运行的心脏了解并分析它对于实现Java虚拟机是非常重要的.作者以Kaffe(一种Java虚拟机的实现)的桌面版本为例,对虚拟机进行较深入地分析.

  1Java虚拟机的运行过程

  Kaffe是一个JVM以及类库的实现,这种实现采用开放源代码的形式m.

  任何Java程序都是由若干个类组成的,其中只有一个主类.源程序是一个.java文件,它经过编译后生成若干个.class文件..class文件的个数由程序中类的个数决定m.

  JVM在执行某个程序时,首先从主类的main方法开始.所以在虚拟机运行的时候,首先要给出主类的名字,然后虚拟机会根据用户提供的名字按照系统设定的class路径寻找该类,并将其装载链接、初始化(如果找不到该类,则报告错误并退出).同时,该类的初始化有可能引起其他类的初始化(因为在一个类初始化之前它的父类必须被初始化).初始化完成后,虚拟机便寻找主类的main方法,并按照用户为main方法提供的参数执行.当main方法的代码成功执行后,虚拟机卸载当前线程并退出.此时整个程序的执行过程完毕.

  用户可以自己设定一些JVM的环境参数,虚拟机程序执行的第1步是处理用户对这些参数的设置,然后开始执行程序.例如,可以利用命令行kaffe_version获得这个虚拟机的版本号,也可以通过kaff一mx设置堆栈的最大容量.其他参数可以通过_help获悉.

  以下是一个Kaffe执行过程的实例:

  欲执行程序,其源程序为:

  blicclasshelloworld{

  publicstaticvoidmain(Stringargs[])

  {

  n(hello

  world);}

  71994-2014ChinaAcadeir}

  Kaffe虚拟机是用C语言实现的,任何C语言程序的执行都是从main函数开始的,Kaffe也不例外.图1是main函数的程序流程图,它也是整个虚拟机的执行流程图.

blob.png

  ①程序执行过程如下:按照该流程,首先应该获得虚拟机环境参数的缺省值.获得该缺省值的功能由函数JNI_GetDefaultJavaVMInitArgs(&vmargs)实现.该函数可将结构Kaffe_JavaVMInitArgs拷贝到vmargs中.

  ②处理命令行中用户设置的参数.该功能由函数staticintoptions(char**)完成.该函数的参数就是main(argc,argv)中的argv,它的返回值是一个整形量,表示该函数处理的参数量.

  ③为虚拟机进行初始化、分配内存.该功能在main中通过调用JNI_CreateJavaVM(&vm,&env,&vmargs)实现.该函数中参数vmags是己经被赋初值的虚拟机参数集合,该函数在完成虚拟机的生成工作后将指向JVM的指针存放在vm中.虚拟机生成的工作主要是申请内存,然后将其中的参数进行缺省设置.

  ④根据用户在命令行中提供的主类名寻找主类并将其装载链接、初始化.这是整个虚拟机执行过程的重点.在函数main()中是通过调用kaffe_Findclass()实现的.kaffe_Findclass()起到了调度的作用,图2是它的流程图.

                            blob.png     blob.png

  可以看出,欲处理的类可能是数组或类.因为在Java中数组是被看作类处理的.函数lookupclass()的功能是寻找指定的非数组类并完成它的初始化.

  lookupclass()的流程图见图3.

blob.png

  Hjava_lang_Class^

  lookupClass(constchar*name,errorInfo*

  einfo)

  {

  Hjava_lang_Class^class;

  Utf8Const*utf8;

  uft8=utf8ConstNew(name,-1);/

  将欲处理的类的名字转化为Utf8形式*/

  if(!utf8){

  postOutOfMemory(einfo);

  return0;

  }

  class=loadClass(utf8,NULL,einfo);/在内

  调用loadclass()完成类的装载*/utf8ConstRelease(utf8);if(class!=0){*如果类己成功装载,调用processclass()*/

  if(processClass(class,CSTATE_COM­PLETE,einfo)==true){

  return(class);

  }

  }

  return(0);

  }

  Lookupclass()函数中所调用的两个子函数loadclass()和processclass()的功能分别是装载和链接、初始化.

  loadclass()的流程图见图4.

blob.png

  

loadclass()首先检查classentry表,该表记录己经被装载类的情况.假设方法1用到类A,则装载类A,若方法2也用到类A,方法2就不必再装载该类.因为方法2通过classentry表得知类A己经被装载.每个classentry表项的内容如下:typedefstruct_classEntry{

  Utf8Const*name;A该表项所表示的类的名字

  Hjava_lang_ClassLoader*loader;II该类被装载时所用的装载器

  Hjava_lang_Class^class;A指向该类在内存中的指针

  struct_classEntry*next;I指向下一个表项的指针

  struct_iLock*lock;I实现查询或更改该表的同步

  jclassEntiy;

  Loadclass()查询classentry表,看所指定的类是否己经被装载.若装载,则返回指向该类的指针;否则,创建一个classentry表项、该类装载完成后使entry->class指向该类.

  查看它的父函数lookupclass()是否给出了具体的装载器.装载器是Java中类classloader及它的子类,它们的功能是完成类的装载.如果给出了装载器,就执行该装载器类的方法.如果lookupclass()没给出具体的loader,则使用findclass()函数■该函数寻找指定类,并完成它的装载,然后返回指向该类的指针.如果loader或findclass()执行不成功,就会给出错误信息并返回空指针.

  Loadclass()完成后,lookupclass()会调用Processclass(),完成对己装载类的链接和初始化.装载、链接、初始化都完成后,对编译后的main方法的字节码逐条加以解释.这里虚拟机解释的是字节码,字节码是Java源程序经过编译后的结果.如果把虚拟机看成是一个真正的机器,那么字节码就是这台机器的汇编语言.按照Sun的Java虚拟机规范,JVM拥有大约220条指令.Java源程序经过编译后所生成的类文件相当于Java源程序的汇编程序,这里的汇编指令是虚拟机的指令.在Kaffe中,逐条解释字节码是通过函数runvirtualmachine()实现的.在中定义了每一条虚拟机指令的解释程序.

  runvirtualmachine()的执行过程是一个循环体很大的循环,该循环如下(忽略异常):

  do{

  fetchanopcode;

  If(operands)fetchoperands;executertheactionfortheopcode;jwhile(thereismoretodo);

  kaffe的实现也是如此,取一条字节码指令后会进入一个大的case语句中,使其与指令集中的220条指令进行匹配,匹配指令的解释程序将被执行.

  3面向堆栈的体系结构

  器中.而Java虚拟机采用了面向堆栈的体系结构.JVM中的堆栈区用来存储操作数和中间结果,传递参数并返回结果,同时也跟踪方法的执行状态.方法的执行状态称为当前方法的堆栈框架(stackframe),它保存方法的当前执行信息(局部变量中间结果等).堆栈框架包括3个部分:局部变量区、操作数栈和执行环境区.局部变量区包含当前方法用到的所有局部变量.操作数栈是字节码指令的工作区,操作数栈顶指针寄存器(optopregister)存放栈顶地址.执行环境区维护栈本身的操作,执行环境寄存器(frameregister)中包含指向它的指针.

  4结束语

  Java是一种解释型语言,逐条解释字节码的过程是比较耗时的,这导致了Java的致命缺点一一速度慢.Symantec,Borland,Microsoft等大公司都在开发JIT(及时)编译器,程序开始执行之前把字节码编译成本地机器码,这样就用字节码编译器代替了翻译器.采用这种技术的Java虚拟机性能大幅度提高,通常比翻译器的速度快V10倍.也许将来JIT编译器会成为Java虚拟机整体的一部分,从而使Java虚拟机性能满足应用的要求.

  Java应用需要解决的主要问题之一是加快虚拟机的运行速度.本文中以一种JVM的源代码为例,分析了虚拟机的运行过程,意在对改进它的运行机制起到一些作用.

 


上一篇:Java动态类加载机制研究及应用

下一篇:Java课程群网络教学平台的构建