首页 > 编程语言 >如何用 Java 写一个 Java 虚拟机

如何用 Java 写一个 Java 虚拟机

时间:2023-07-28 19:56:13浏览次数:36  
标签:Java String 虚拟机 jar class 如何 haidnorJVM java public

github 项目链接

https://github.com/FranzHaidnor/haidnorJVM

haidnorJVM

使用 Java17 编写的 Java 虚拟机

意义

  1. 纸上得来终觉浅,绝知此事要躬行。只学习 JVM 机制和理论,很多时候任然觉得缺乏那种大彻大悟之感
  2. 使用简单的方式实现 JVM,用于学习理解 JVM 运行原理

主要技术选型

实现功能

  • 实现了 99% 的 JVM 字节码指令。参照 JVM 字节码规范实现 The Java Virtual Machine Instruction Set
  • 支持算数运算符 (+,-,*,^,%,++,--)
  • 支持关系运算符 (==,!=,>,<,>=,<=)
  • 支持位运算符 (&,|,^,~,<<,>>,>>>)
  • 支持赋值运算符 (=,+=,-=,*=,%=,<<=,>>=,&=,^=,|=)
  • 支持 instanceof 运算符
  • 支持循环结构代码 (while,do...while,for,foreach)
  • 支持条件结构代码 (if,if...else,if...else if)
  • 支出创建自定义类
  • 支持创建对象、访问对象
  • 支持抽象类
  • 支持多态继承、接口
  • 支持访问静态方法
  • 支持访问对象方法
  • 支持 JDK 中自带的 Java 类
  • 支持反射
  • 支持异常
  • 枚举 (开发中...)
  • switch 语法 (开发中...)
  • lambda 表达式 (开发中...)

局限性

  • 不支持多线程
  • 不支持多维数组
  • 暂无双亲委派机制实现
  • 无垃圾收集器实现。垃圾回收依靠宿主 JVM

快速体验

你需要准备什么

  1. 集成开发环境 (IDE)。你可以选择包括 IntelliJ IDEA、Visual Studio Code 或 Eclipse 等等
  2. JDK 17。并配置 JAVA_HOME
  3. JDK 8。haidnorJVM 的主要目标是运行 Java8 本版的字节码文件。(但 haidnorJVM 没有强制要求字节码文件是 Java8 版本)
  4. Maven

配置 haidnorJVM

配置日志输出级别

resources\simplelogger.properties 文件中修改日志输出级别,一般使用 debuginfo

  • 配置 info 级别将不会看到任何 haidnorJVM 内部运行信息
  • 配置 debug 级别下运行将会非常友好的输出 JVM 正在执行的栈信息
public class Demo5 {

    public static void main(String[] args) {
        String str = method1("hello world");
        method1(str);
    }

    public static String method1(String s) {
        return method2(s);
    }

    public static String method2(String s) {
        return method3(s);
    }

    public static String method3(String s) {
        System.out.println(s);
        return "你好 世界";
    }
    
}

每一个 结构图形,都表示一个 JVM 线程栈中的栈帧

配置 rt.jar 路径

修改 haidnorJVM.properties 文件中的内容。配置 rt.jar 的绝对路径,例如rt.jar=D:/Program Files/Java/jdk1.8.0_361/jre/lib/rt.jar

运行单元测试用例

在 IDE 中打开项目中 test 目录下的 haidnor.jvm.test.TestJVM.java 文件。 这是 haidnorJVM 的主要测试类, 里面的测试方法可以解析加载运行 .class 字节码文件。

public class TestJVM {
   /**
    *  haidnorJVM 会加载 HelloWorld.java 在 target 目录下的编译后的字节码文件,然后运行其中的 `main(String[] args)` 方法。
    *  你可以使用打断点的方式看到 haidnorJVM 是如何解释运行 Java 字节码的。
    *  值得注意的是,这种方式编译运行的字节码文件是基于 java17 版本的。
    */
   @Test
   public void test() {
      runMainClass(HelloWorld.class);
   }
}

运行 .class 文件

  1. 使用 maven 命令将 haidnorJVM 编译打包,得到 haidnorJVM.jar 文件
  2. 编写一个简单的程序,例如以下代码
public class HelloWorld {
   public static void main(String[] args) {
     System.out.println("HelloWorld");
   }
}
  1. 编译代码,得到 HelloWorld.class 文件 (推荐使用 JDK8 进行编译)
  2. 使用 haidnorJVM 运行程序。执行命令 java -jar haidnorJVM.jar -class R:\HelloWorld.class。注意! 需要 class 文件的绝对路径

运行 .jar 文件

  1. 使用 maven 命令将 haidnorJVM 编译打包,得到 haidnorJVM.jar 文件
  2. 编写一个 java 项目编译打包成 .jar 文件,例如 demo.jar。要求 .jar 文件中的 META-INF/MANIFEST.MF 文件内有 Main-Class 属性 (含有 public static void main(String[] args) 方法的主类信息)
  3. 使用 haidnorJVM 运行程序。执行命令 java -jar haidnorJVM.jar -class R:\demo.jar。注意! 需要 jar 文件的绝对路径

存在的问题

由于 haidnorJVM 目前运行 JDK 自带的类是使用反射解决的,因此 haidnorJVM 使用 JDK17 运行部分 JDK 自带的类时会存在一些问题,例如运行以下代码将会抛出异常

public class Demo {
    public static void main(String[] args) {
        List<Integer> list = List.of(1, 2, 3, 4, 5);
        list.add(6);
    }
}
java.lang.reflect.InaccessibleObjectException: Unable to make public boolean java.util.ImmutableCollections$AbstractImmutableCollection.add(java.lang.Object) accessible: module java.base does not "opens java.util" to unnamed module @18769467

	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
	at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199)
	at java.base/java.lang.reflect.Method.setAccessible(Method.java:193)

它表示尝试通过反射来访问一个方法或字段,但该方法或字段的可访问性限制导致无法访问。

这个限制通常是由于 Java 模块系统引起的。模块系统允许将代码划分为独立的模块,
并控制模块之间的访问权限。以上异常的原因是 module java.base does not "opens java.util" to unnamed module,也就是说 java.base 模块没有向未命名模块开放 java.util 包

解决方法:
启动 haidnorJVM 时添加 JVM 参数 --add-opens java.base/java.util=ALL-UNNAMED 绕过访问性限制

联系作者


微信号: haidnor

标签:Java,String,虚拟机,jar,class,如何,haidnorJVM,java,public
From: https://www.cnblogs.com/Haidnor/p/17588772.html

相关文章

  • 多个账号如何同时远程登陆Windows11(开启home版远程登陆)
    首先去下面链接下载最新版的RDPWrap,然后解压https://github.com/stascorp/rdpwrap/releases 右键以管理员身份运行 install.bat运行完成之后运行RDPConf.exe,如果全绿,那么就没问题了如果遇到像我这个同样的问题,那么就需要重新下载一个rdpwrap.ini 文件并且替换掉C:\Progra......
  • javaScript判断数据类型的几种方法
    1:typeof返回数据类型,包含这7种:number、boolean、symbol、string、object、undefined、function。typeofnull返回类型错误,返回object。引用类型,除了function返回function类型外,其他均返回object。其中,null有属于自己的数据类型Null,引用类型中的数组、日期、正则也都有......
  • Java 理论和实践: 了解泛型
    级别:初级2005年1月25日Java理论和实践”中,BrianGoetz分析了束缚第一次使用泛型的用户的常见陷阱。您可以通过讨论论坛与作者和其他读者分享您对本文的看法。(也可以单击本文顶端或底端的讨论来访问这个论坛。)表面上看起来,无论语法还是应用的环境(比......
  • Windows本地IDEA运行mapreduce报错java.io.FileNotFoundException: HADOOP_HOME and h
    问题原因在windows运行hadoopJob程序的时候需要模拟下hadoop的运行环境。否则出现会出现标题的问题。解决方案下载Hadoop的bin目录https://github.com/s911415/apache-hadoop-3.1.3-winutils将步骤1中下载的文件配置成环境变量HADOOP_HOME(指向解压之后的的bin的上级目录)。......
  • 利用JAVA操作EXCEL文件
    级别:初级2003年1月11日使用Windows操作系统的朋友对Excel(电子表格)一定不会陌生,但是要使用Java语言来操纵Excel文件并不是一件容易的事。在Web应用日益盛行的今天,通过Web来操作Excel文件的需求越来越强烈,目前较为流行的操作是在JSP或Servlet中创建一个CSV(commasepa......
  • Java开发 - SpringCache初体验
    前言早些时候,博主介绍过Redis的使用:Java开发-Redis初体验,Redie是基于缓存的一项技术,对于Redis,博主此处不再赘述,不了解的可以去看这篇文章,但Redis缓存并不是顶峰,本文要讲的内容就是Redis的辅助工具:SpringCache——的使用。有了SpringCache,Redis便可如虎添翼,使用效果更上一层楼,下面......
  • 信创啊,信创。Solon 的 war 包,现在同时支持 jakarta.servlet(及 javax.servlet)容器了!
    Solon是个神奇的项目,不是基于Servlet的。但是又很支持Servlet,尤其是war包。打起来还挺方便的。如果你是做信创的(听说,很多信创项目是用war部署到tomcat容器下的)。自从javaee改包名后,那个苦啊。但是,Solon可以用一样的开发,双同时支持:javax.servletjakarta.servlet......
  • java中常见的中文编码格式
    几种常见的编码格式为什么要编码首先要了解为什么要编码?我们能不能不编码?要回答这个问题必须要回到计算机是如何表示我们人类能够理解的符号的,这些符号也就是我们人类使用的语言。由于人类的语言有太多,因而表示这些语言的符号太多,无法用计算机中一个基本的存储单元——byte来......
  • 通过 Javacore 诊断线程挂起等性能问题
    Javacore与WebSphereCommerce性能问题近年来,依据WebSphereCommerce(以下简称为WC)搭建的电子商务网站系统日益增多。由于系统本身的复杂性,一旦系统出现问题,尤其是性能问题,问题诊断和定位就会非常困难。下图所示为由WC系统为核心搭建的电子商务网站的一般逻辑架构,如图......
  • Java整理
    1.String类的特点Java程序中所有双引号字符串,都是String这个类的对象字符串一但被创建,就不可修改,字符串内容不可改变如果想要更改,创建新的对象替换Strings1="abc";s1="bcd"-String字符串虽然不可改变,但是可以共享字符串常量池:当我们使用双引号创建对象,会在常量池中......