Java虚拟机的研究与实现

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情
Java虚拟机本质是就是一个程序,当它在命令行上启动的时候,就开始执行保存在某字节码文件中的指令。Java语言的可移植性正是建立在Java虚拟机 的基础上。任何平台只要装有针对于该平台的Java虚拟机,字节码文件(.class)就可以在该平台上运行。这就是“一次编译,多次运行”。

  kaffe虚拟机的简要分析

  kaffe虚拟机采用了模块化的程序设计思想,它由多个独立的子系统组成。从功能模块上来分它主要分为:虚拟机总体驱动模块,类装载器模块,类执行模块, 数据区管理模块,内存管理模块,本地支持模块等等。kaffe虚拟机简要的程序流程图如图1所示。

  
  图1 kaffe虚拟机简要的程序流程图

  Java虚拟机的实现

  Java 源程序的执行过程为: Java源程序(.java)经过Java编译器编译生成字节码文件(.class),然后由类装载器将字节码文件装载到方法区中,然后进行连接验证,由Java虚拟机读取字节码,转换为特定平台的指令,并且在对应的CPU中执行。

  本实现中采用的流程框架如下图所示:

  
  图2 本实现的主要框架

  1、类装载、连接及初始化

  类文件包括:魔数(magic),次、主版本号,常量池,类或接口访问修饰符,常量池索引(this_class和super_class),接口表,域表,方法表,类或接口的属性信息。其中最复杂的内容是常量池,它类似于传统语言编译过程中用到的符号表。

  从原始的class文件到可以被Java虚拟机执行的内部数据格式,需要经过装载、连接和初始化这3个阶段。

  装载是将class文件通过类装载器装载到在逻辑上被称为方法区的内存单元中的过程。

   连接又分为三个步骤:验证,准备和解析。验证是对字节码的验证,可根据具体情况来确定被装载的类是否符合Java虚拟机规范中规定的class文件格 式,并确保它不会破坏Java虚拟机的完整性。包括(1)类装载过程中的验证; (2) 检查class文件内部的连贯性,一旦发现class文件格式存在一处错误,则抛出VerifyError异常或ClassFormatError异常。 确保每个final类不含有子类,final方法不能被覆盖,以及常量池中所有的域引用和方法引用有有效的名字和类型描述符号;(3) 对字节码流使用一个数据流分析器进行验证。准备步骤的任务是创建域表,并设置域初值。解析步骤是将类中的常量池中的类、接口、字段和方法的符号引用替换成 直接引用,以达到更快地访问数据的目的。

  在初始化阶段,Java虚拟机设计者需要将类变量赋予正确的初始值。

  class文件经过上述三个阶段的处理,虚拟机就获得了该类的所有信息并且表示成能够容易操作的内部数据格式,从而为方法的运行作好了充分的准备。
2、及时编译器

  任何Java虚拟机实现的核心都是它的执行引擎。在由软件实现的虚拟机中,执行引擎主要有一次性解释字节码、及时编译 器、自适应优化编译器三种方式。本实现采用了及时编译的方式,它的特点是第一次被执行的机器码会被编译成本地机器码。及时编译器将引入的字节码翻译成本地 机器码,然后直接执行机器码指令而不是解释字节码。机器码指令保存在内存中,由于在运行过程中编译的结果不被保存, 所以程序下一次运行时,字节码将再一次被翻译成机器码。

  如果一装载完字节码文件中的Java方法后,就对其进行编译,则有点处理不恰 当,因为还不清楚是否需要执行该方法。编译一个不需要执行的方法,将带来不必要的空间和时间上的损失。 因此虚拟机设计者需要采用一种优化方案,即只有需要被执行的方法才能被JIT编译,这个问题可以参照kaffe虚拟机中的trampoline来解决。

  JIT实现步骤:(1)对字节码进行验证并且划分基本块;(2)产生四元式;(3)根据四元式生成本地机器码;(4)操作数地址回填。

  
  图3及时编译器的流程图

  在字节码指令模拟操作的时候,按其语义动作生成指令属性四元式序列,指令属性四元式的结构为: (目的操作数, 源操作数1,源操作数2,语义动作),四元式数据结构如下:

typedef struct Sequence{
 void (*func)(struct Sequence*); //语义动作
 union{
  jvalue value;
  struct _label_ *labconst; //标号类型操作数
  Method *methconst; //方法地址操作数
  struct slotData **smask;
  struct slotData *slot; //槽操作数
 }u[3];
 uint8 type; //Sequence类型
 uint8 refered; //该四元式的引用
 struct Sequence *next; //下一个四元式
}Sequence;

  其中目的操作数为Sequence.u[0],源操作数1为Sequence.u[1],源操作数2为Sequence.u[2]。 Sequence.func则代表语义动作,它主要用于生成该Sequence语义的本地机器码。

  指令属性四元组建立后就进入代码生成阶段,属性四元组在形式上已经非常接近本地机器指令,只需要遍历该属性序列,执行相应的语义动作函数,即可生成机器指令。语义动作函数的功能包括操作数寻址、寄存器分配、建立指令连接以及本地机器码生成等。

  在及时编译过程中要经常使用到操作数栈,虚拟机把操作数栈作为它的工作区。大多数指令都要从这里弹出数据,执行运算,然后把结果压回操作数栈。而操作数栈区,局部变量区和帧数据区被包含在方法帧中。方法帧的数据结构如下:

typedef struct Frame{
 struct Frame *prev; // 上一帧
 struct Frame *next; // 下一帧
 value_t *sp; // 栈槽指针
 uint8 *pc; // 程序计数器
 method_t *method; //指向正在被执行的方法
 class_t *class_ptr; // 指向包含该方法的类
 value_t locals[1]; //方法的局部变量的起始
}Frame;

   本实现中的及时编译器的优点表现在:(1)大大提高了Java应用程序运行的速度;(2)编译过程只在运行时进行,不会改动Java字节码,不会影响 Java程序的可移植性;(3)对字节码的编译,使得许多优化手段的采用成为可能。缺点表现在:(1)如果对所有方法进行编译,则会占用大量的内存空间; (2)及时编译的结果在虚拟机终止运行时不被保存,这意味着下一次运行同样的程序仍需要重复编译。
3、垃圾收集

  垃圾收集器主要的任务是检测出垃圾对象,然后回收垃圾对象使用的堆空间并还给程序。kaffe采用了增量垃圾收集的算法,而本实现中采用了三色标记并清除算法。

   在标记之前先将堆中所有的分配单元置成白色,然后按深度优先算法遍历每一个单元。当垃圾收集器遍历一个分支的时候,如果一个分配单元及与之相关联的单元 都被遍历到,则将其标记成黑色。 如果一个单元被遍历到,但是与之相关联的单元尚未被遍历,则将该单元标记成灰色。这时,垃圾收集器将继续遍历与该灰色单元相关联的单元,直到这些相关联的 单元全部被遍历到,才能将这个灰色单元标记成黑色。最后当所有被遍历到的单元都被标记成黑色的时候, 将堆中被标记成白色的分配单元回收。

  
  图4三色标记并清除算法的中间过程图

  最后是对堆碎块进行压缩处理。是通过快速地移动对象来减少堆碎块。即把当前活动的对象移动到堆的一端,在此过程中,堆的另外一端出现一块大的连续的内存单元。所有被移动的对象的引用也被更新,指向新的内存单元。

  4、线程同步

   Java虚拟机中存在着以下两种线程:虚拟机系统线程和用户Java线程。虚拟机系统线程是指虚拟机运行过程中执行其特殊功能的线程,比如垃圾收集器线 程等。用户Java线程是指用户编写的Java应用程序中明确表示要启动的线程,并且至少有一个Java线程,即main方法。

  而 Java语言的一大优势是支持多线程,这种支持主要表现在同步上。在java应用程序中使用synchronized关键字简单地使方法同步,而在 Java虚拟机指令中则使用monitorenter和monitorexit指令显式地支持方法同步。Java虚拟机为每个对象都关联一个锁。当当前线 程访问共享资源的时候,会执行monitorenter指令来弹出该对象引用,从而获取该对象引用相关联的锁。如果该对象已经被另一线程占用则当前线程就 需要进入锁的等待队列,等待释放对象上的锁;已经获取共享资源的线程在释放资源的时候,执行monitorexit指令来弹出对象引用,并且释放与该资源 相关联的锁,并让等待队列中的第一个线程获取该对象锁。

  当然线程thread也可以根据需要对某对象obj多次上锁,上锁的次数放在计数器counter中。只有当counter为0的时候,即thread加在该对象上的锁被完全释放,其它线程才有机会使用 object。对象的数据结构为:

typedef struct Obj{
 uint32 size; //堆中对象的大小
 uint16 counter; //对象被上锁的数量
 uint16 flag; //对象的状态标志
 uint16 thread_id; //对该对象进行加锁的
 //线程的ID
} Obj;

  而在实际Java编程中, 程序员并不需要动手加锁,对象锁只是在Java虚拟机内部使用的。程序员只需要编写同步语句就可以标志一监视区域,当Java虚拟机运行程序的时候,每次进入一个监视区域,它每次都会给对象上锁。

  5、线程调度

  在本实现中,还需要考虑到在上述等待线程队列中如何选择下一个线程来执行,即线程调度问题。

   哪个线程将获取notify命令,这一点在很大程度上取决于虚拟机的设计者,既可以通过使用FIFO队列来调度,也可以根据所有等待线程的优先级来调 度,比如唤醒等待队列中优先级最高的线程获取刚刚释放的资源。而Bill Venners则从平台无关和执行效率这两个角度出发,提倡Java虚拟机的设计者应使用java.lang.Object类中的notifyAll() 方法来代替notify()方法去唤醒等待队列中的线程。

  处理好线程调度问题,就可以节省程序的执行时间,这对于提高Java虚拟机的执行性能是很有帮助的。

  总结

   本文在研究kaffe的基础上,实现了一虚拟机。并且对Java虚拟机中的关键技术及时编译器、垃圾收集器、线程同步和线程调度等做了分析。本文中所实 现的及时编译器虽然在执行速度上比解释型的Java虚拟机快得多,但是不如自适应优化编译器,因为自适应优化编译器具有程序启动快,占用内存少的特点。如 果要明显地提高虚拟机的性能,应该更多的从执行引擎着手。另外在Java应用程序的执行过程中许多时间是花费在多线程处理和垃圾收集上,如果在线程同步和 线程调度上有所创新,也可以提高虚拟机的执行性能。

  研究Java虚拟机的实现过程有重要的意义,程序员可以编写针对于不同平台下的裁减了的Java虚拟机,这样它就可以在实时嵌入式系统得到广泛地应用。相信Java虚拟机将在更多的领域得到不断的完善和发展
一个简单的Java虚拟机实现(v032) 这是一个简单的Java虚拟机实现,用C++写的。可以为它编写和运行简单的java程序。这个程序是写给初学者学习用的,当然它还不完善,指令也还没有完全实现。只希望它对于初学者学习Java虚拟机有所帮助。 立即下载

相关推荐

一个Java虚拟机的设计和实现

本文叙述了Java虚拟机(JVM)的概念及如何设计和实现一个Java虚拟机——MiniJavaVM。着重介绍了虚拟机的体系结构及如何设计和实现这个体系结构。在探讨虚拟机的设计过程中详细介绍了MiniJavaVM虚拟机各部分的设计,包括类的装载和解析,内存管理,执行引擎,方法调用和异常处理部分。最后通过测试MiniJavaVM来验证设计和实现的正确性。

虚拟机单机环境搭建Kafka集群

zookeeper需要java运行环境。

优秀的程序员不应该是CRUD 710

Java虚拟机及其移植

探索并实现一个基于嵌入式应用平台的java虚拟机

JAVA 虚拟机概念和实现原理

了解JAVA AVM 1、什么是 Java 虚拟机?为什么 Java 被称作是“平台无关的编程语言”? Java 虚拟机是一个可以执行 Java 字节码的虚拟机进程。Java 源文件被编译成能被 Java 虚拟机执行的字节码文件。 Java 被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一个平台单独重写或者是重新编译。Java 虚拟机让这个变为可能,因为它知道底层硬件平台的指令长度和其他特性。 2、虚拟机是如何启动的? 3、Class文件结构 Java class文件是8位字节的二进制流,数

liutongas的博客 1243

开源项目推荐:Kaffe虚拟机

开源项目推荐:Kaffe虚拟机 项目基础介绍及编程语言 Kaffe虚拟机 是一个遵循GNU Public License(GPL)的开源Java虚拟机。这个项目起始于对自由软件社区的巨大贡献,它使开发者能够执行Java代码而不受商业限制。Kaffe主要采用C++进行开发,这使得它在多个平台上具有高度的可移植性。 核心功能 Kaffe的核心功能在于提供了一个兼容Java规范的运行环境,支持Java字...

gitblog_00242的博客 555

Java虚拟机

JVM(Java虚拟机)基本知识

go back 1320

Java 虚拟机实战(基础篇 1万字)

JVM 本质上是一个运行在计算机上的程序,他的职责是运行Java 字节码文件类的生命周期描述了一个类加载、使用、、卸载的整个过程加载(Loading) 阶段第一步是类加载器根据类的全限定名通过不同的渠道以二进制流的方式获取字节码信息。程序员可以使用 Java 代码拓展的不同的渠道类加载器在加载完类之后,Java虚拟机 会将字节码中的信息保存到方法区中类加载器在加载完类之后,Java虚拟机 会将字节码中的信息保存到内存的方法区中。

2301_79083000的博客 1823

Java虚拟机研究实现1

摘 要 本文在研究kaffe的基础上,吸收kaffe虚拟机的主要思想,用C语言作为开发语言,采用了及时编译器作为执行引擎,实现了一种Windows平台下 的Java虚拟机。然后对实现过程中的一些关键技术如class文件验证、及时编译器、垃圾收集器、线程同步和线程调度等做了分析。  关键词 kaffe; C语言; 及时编译器;Java虚拟机  引言   Java虚拟机本质是就是一个程序,当它在命令行

powerbuddhist的专栏 1093

Java虚拟机研究实现3

4、线程同步  Java虚拟机中存在着以下两种线程:虚拟机系统线程和用户Java线程。虚拟机系统线程是指虚拟机运行过程中执行其特 殊功能的线程,比如垃圾收集器线程等。用户Java线程是指用户编写的Java应用程序中明确表示要启动的线程,并且至少有一个Java线程,即main 方法。  而 Java语言的一大优势是支持多线程,这种支持主要表现在同步上。在java应用程序中使用synchronized

powerbuddhist的专栏 727

kaffe java_Java虚拟机Kaffe的研究实现

摘要本文在研究kaffe的基础上,吸收kaffe虚拟机的主要思想,用C语言作为开发语言,采用了及时编译器作为执行引擎,实现了一种Windows平台下的Java虚拟机。然后对实现过程中的一些关键技术如class文件验证、及时编译器、垃圾收集器、线程同步和线程调度等做了分析。关键词kaffe; C语言;及时编译器;Java虚拟机引言Java虚拟机本质是就是一个程序,当它在命令行上启动的时候,就开始执行...

weixin_29616999的博客 181

MiniJavaVM——一个Java虚拟机的设计和实现

博文链接:https://jimi68.iteye.com/blog/176107

Java虚拟机的设计和实现

Java虚拟机的设计和实现

java虚拟机实现_Java虚拟机研究实现(2)

连接又分为三个步骤:验证,准备和解析。验证是对字节码的验证,可根据具体情况来确定被装载的类是否符合Java虚拟机规范中规定的class文件格式,并确保它不会破坏Java虚拟机的完整性。包括(1)类装载过程中的验证; (2) 检查class文件内部的连贯性,一旦发现class文件格式存在一处错误,则抛出VerifyError异常或ClassFormatError异常。确保每个final类不含有子类,...

weixin_42387319的博客 161

java 虚拟机研究实现

2006-9-25 11:08:12 作者:夏兵 俞建军(79) 引言   Java虚拟机本质是就是一个程序,当它在命令行上启动的时候,就开始执行保存在某字节码文件中的指令。Java语言的可移植性正是建立在Java虚拟机的基础上。任何平台只要装有针对于该平台的Java虚拟机,字节码文件(.class)就可以在该平台上运行。这就是“一次编译,多次运行”。   kaffe虚拟机的简要

分享美好的专栏 904

Java虚拟机Kaffe的研究实现

<br />http://blog.sina.com.cn/s/blog_4be8ae2c01000a9h.html<br /> <br />摘要     本文在研究kaffe的基础上,吸收kaffe虚拟机的主要思想,用C语言作为开发语言,采用了及时编译器作为执行引擎,实现了一种Windows平台下的Java虚拟机。然后对实现过程中的一些关键技术如class文件验证、及时编译器、垃圾收集器、线程同步和线程调度等做了分析。 关键词 kaffe; C语言; 及时编译器;Java虚拟机<br /><br />引言

792

Java虚拟机研究实现2

2、及时编译器  任何Java虚拟机实现的核心都是它的执行引擎。在由软件实现虚拟机中,执行引擎主要有一次性解释字节码、及 时编译器、自适应优化编译器三种方式。本实现采用了及时编译的方式,它的特点是第一次被执行的机器码会被编译成本地机器码。及时编译器将引入的字节码翻译 成本地机器码,然后直接执行机器码指令而不是解释字节码。机器码指令保存在内存中,由于在运行过程中编译的结果不被保存,所以程序下一次运

powerbuddhist的专栏 826
上一篇: java实现树
下一篇: 用YACC/LEX 设计计算机语言
judyge
博客等级 码龄12年 612粉丝 286原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值