从零开始,自己动手写Java虚拟机

从零开始,自己动手写Java虚拟机

前言

自己动手写一个Java虚拟机, 难吗? 很难! 很难吗?不难!
答案不同,那是因为对Java虚拟机用途定位不同。
难! 如果自己写的java虚拟机能完整的实现规范,达到商用的性能和稳定性。
不难!如果不考虑规范和性能,只是运行简单程序,了解java底层技术。

天下事有难易乎?为之,则难者亦易矣;不为,则难者亦难矣

java虚拟机的作用

java虚拟机的作用就是解释class文件中的java指令。每一条java指令,java虚拟机规范中都有详细定义,如怎么取操作数,怎么处理操作数,处理结果放在哪里。

java虚拟机的组成

一个最最简单的虚拟机demo只需要类加载器和执行引擎。
类加载器负责解析class文件,获取类的信息,如常量信息,变量信息,Java方法的指令等等,并将这些信息有组织的存在内存中,通常这个内存区域被称为方法区。
执行引擎负责按照虚拟机规范,逐条解释指令,java虚拟机是基于栈的,大部分操作数都是从操作数栈和变量表中取,并将结果存放在操作数栈中。

笔者在虚拟机demo中实现bootstrap加载器,用来加载运行时class文件(rt.jar)和用户自己的class文件,并没有实现规范中双亲委托加载模式,让系统class文件的包名用java开头(如java.lang, java.io), 而用户的class文件包名不允许java开头感觉也能避免恶意替换系统class文件的隐患。

一些成熟的商用java虚拟机实现中,方便管理和明确用途,会把内存分成不同的区域:
1. 如前面提到的方法区。
2. pc寄存器,存储下一条执行指令的地址, 其实这是一个逻辑概念。如果运行时有办法获取下一条指令的地址,也可以没有pc寄存器呢。但是在多线程,各种跳转的情况下,运行时很难获取下一条指令的地址,pc寄存器一般是和线程相关的。
3. java栈,实现java方法的调用,传参,局部变量存储,函数返回等,java栈也是和线程相关的。
4. 本地栈,实现native函数调用。
5. 存储对象的堆,这个是垃圾回收器重点关注的区域。

java虚拟机还有一个重要模块是垃圾回收器,就像令人尊敬的环卫工作人员一样,默默的处理了一些糟糕杂乱的垃圾,让你拥有更好的生活环境。
java之父詹姆斯大叔为了简化程序员的工作,提高开发效率, 程序员只能负责对象的出生,却不能宣判对象的死亡。每个对象默认都是长生不老,只要内存足够,就会一直存活。 但是实际上,资源都是有限的,内存不足时,没有回收一些无用的对象释放内存, 新生的对象就没有内存存储。
垃圾回收器就是定期或者说是某种条件下触发开关,执行清理不再使用的对象,释放内存的任务。
所以感谢虚拟机,感谢垃圾回收器吧。 没有垃圾回收器,java也许会和cpp一样, 各种悬指针,野指针,泄露问题让你头疼不已。

java虚拟机的开发

  1. 语言选取,不考虑性能,理论上都可以。 用java语言或者脚本实现java虚拟机都没问题。只要能够处理文件io就行。
  2. 标准rt.jar很庞大,基本会覆盖所有指令。开发虚拟机时可以自己实现简易版的rt.jar, 简单的实现一些运行时class。 由于重点不是写rt.jar,可以暂时由native实现。为了更好的移植性,native最好只实现OS相关的代码。
    为了调试方便, 笔者选择了用C语言。

java虚拟机的测试

  1. 解析标准rt.jar.
  2. 运行简单小程序。
  3. java指令覆盖率。

java虚拟机的优化

  1. 类查找, 为了省事,实现时用动态数组存储,查找只能遍历。提高查找效率,可以用map存储。
  2. 指令实现,还有很多指令没有实现,为了可读性,每一个指令都是用一个native函数实现的,效率很低。可以学习JIT等技术。
  3. 多线程支持,为了能快速运行java小程序没有支持多线程.
  4. pc寄存器,之前没有理清思路,所以没有pc寄存器。所以暂不支持goto switch throws 等指令。
  5. 没有实现debugger便于开发时调试。
  6. 自己实现的rt.jar基本没有java代码,都是native实现。非OS相关的,尽量用java实现,方便移植,native实现最小核心功能。
  7. 垃圾回收器没有实现。

广告

笔者实现的其实算不上是一个java虚拟机,充其量算是一个虚拟机demo, 目前能运行一些简单的java程序。还有很多地方需要完善,优化。欢迎有兴趣的朋友一起探讨,学习。
github仓库地址 jvm src repo

你可能感兴趣的:(算法,java,C/C++)