利用Java編寫一個(gè)Java虛擬機(jī)
github 項(xiàng)目鏈接
https://github.com/FranzHaidnor/haidnorJVM
haidnorJVM
使用 Java17 編寫的 Java 虛擬機(jī)
意義
- 紙上得來終覺淺,絕知此事要躬行。只學(xué)習(xí) JVM 機(jī)制和理論,很多時(shí)候任然覺得缺乏那種大徹大悟之感
- 使用簡(jiǎn)單的方式實(shí)現(xiàn) JVM,用于學(xué)習(xí)理解 JVM 運(yùn)行原理
主要技術(shù)選型
實(shí)現(xiàn)功能
- 實(shí)現(xiàn)了 99% 的 JVM 字節(jié)碼指令。參照 JVM 字節(jié)碼規(guī)范實(shí)現(xiàn) The Java Virtual Machine Instruction Set
- 支持算數(shù)運(yùn)算符 (
+,-,*,^,%,++,--) - 支持關(guān)系運(yùn)算符 (
==,!=,>,<,>=,<=) - 支持位運(yùn)算符 (
&,|,^,~,<<,>>,>>>) - 支持賦值運(yùn)算符 (
=,+=,-=,*=,%=,<<=,>>=,&=,^=,|=) - 支持 instanceof 運(yùn)算符
- 支持循環(huán)結(jié)構(gòu)代碼 (
while,do...while,for,foreach) - 支持條件結(jié)構(gòu)代碼 (
if,if...else,if...else if) - 支出創(chuàng)建自定義類
- 支持創(chuàng)建對(duì)象、訪問對(duì)象
- 支持抽象類
- 支持多態(tài)繼承、接口
- 支持訪問靜態(tài)方法
- 支持訪問對(duì)象方法
- 支持 JDK 中自帶的 Java 類
- 支持反射
- 支持異常
- 枚舉 (開發(fā)中...)
- switch 語法 (開發(fā)中...)
- lambda 表達(dá)式 (開發(fā)中...)
局限性
不支持多線程
不支持多維數(shù)組
暫無雙親委派機(jī)制實(shí)現(xiàn)
無垃圾收集器實(shí)現(xiàn)。垃圾回收依靠宿主 JVM
快速體驗(yàn)
你需要準(zhǔn)備什么
- 集成開發(fā)環(huán)境 (IDE)。你可以選擇包括 IntelliJ IDEA、Visual Studio Code 或 Eclipse 等等
- JDK 17。并配置 JAVA_HOME
- JDK 8。haidnorJVM 的主要目標(biāo)是運(yùn)行 Java8 本版的字節(jié)碼文件。(但 haidnorJVM 沒有強(qiáng)制要求字節(jié)碼文件是 Java8 版本)
- Maven
配置 haidnorJVM
配置日志輸出級(jí)別
在 resources\simplelogger.properties 文件中修改日志輸出級(jí)別,一般使用 debug、info
- 配置 info 級(jí)別將不會(huì)看到任何 haidnorJVM 內(nèi)部運(yùn)行信息
- 配置 debug 級(jí)別下運(yùn)行將會(huì)非常友好的輸出 JVM 正在執(zhí)行的棧信息
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 "你好 世界";
}
}每一個(gè) 匚 結(jié)構(gòu)圖形,都表示一個(gè) JVM 線程棧中的棧幀

配置 rt.jar 路徑
修改 haidnorJVM.properties 文件中的內(nèi)容。配置 rt.jar 的絕對(duì)路徑,例如rt.jar=D:/Program Files/Java/jdk1.8.0_361/jre/lib/rt.jar
運(yùn)行單元測(cè)試用例
在 IDE 中打開項(xiàng)目中 test 目錄下的 haidnor.jvm.test.TestJVM.java 文件。 這是 haidnorJVM 的主要測(cè)試類, 里面的測(cè)試方法可以解析加載運(yùn)行 .class 字節(jié)碼文件。
public class TestJVM {
/**
* haidnorJVM 會(huì)加載 HelloWorld.java 在 target 目錄下的編譯后的字節(jié)碼文件,然后運(yùn)行其中的 `main(String[] args)` 方法。
* 你可以使用打斷點(diǎn)的方式看到 haidnorJVM 是如何解釋運(yùn)行 Java 字節(jié)碼的。
* 值得注意的是,這種方式編譯運(yùn)行的字節(jié)碼文件是基于 java17 版本的。
*/
@Test
public void test() {
runMainClass(HelloWorld.class);
}
}運(yùn)行 .class 文件
- 使用 maven 命令將 haidnorJVM 編譯打包,得到
haidnorJVM.jar文件 - 編寫一個(gè)簡(jiǎn)單的程序,例如以下代碼
public class HelloWorld {
public static void main(String[] args) {
System.out.println("HelloWorld");
}
}- 編譯代碼,得到 HelloWorld.class 文件 (推薦使用 JDK8 進(jìn)行編譯)
- 使用 haidnorJVM 運(yùn)行程序。執(zhí)行命令
java -jar haidnorJVM.jar -class R:\HelloWorld.class。注意! 需要 class 文件的絕對(duì)路徑
運(yùn)行 .jar 文件
- 使用 maven 命令將 haidnorJVM 編譯打包,得到
haidnorJVM.jar文件 - 編寫一個(gè) java 項(xiàng)目編譯打包成 .jar 文件,例如 demo.jar。要求 .jar 文件中的 META-INF/MANIFEST.MF 文件內(nèi)有
Main-Class屬性 (含有public static void main(String[] args)方法的主類信息) - 使用 haidnorJVM 運(yùn)行程序。執(zhí)行命令
java -jar haidnorJVM.jar -class R:\demo.jar。注意! 需要 jar 文件的絕對(duì)路徑
存在的問題
由于 haidnorJVM 目前運(yùn)行 JDK 自帶的類是使用反射解決的,因此 haidnorJVM 使用 JDK17 運(yùn)行部分 JDK 自帶的類時(shí)會(huì)存在一些問題,例如運(yùn)行以下代碼將會(huì)拋出異常
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)
它表示嘗試通過反射來訪問一個(gè)方法或字段,但該方法或字段的可訪問性限制導(dǎo)致無法訪問。
這個(gè)限制通常是由于 Java 模塊系統(tǒng)引起的。模塊系統(tǒng)允許將代碼劃分為獨(dú)立的模塊,并控制模塊之間的訪問權(quán)限。以上異常的原因是 module java.base does not "opens java.util" to unnamed module,也就是說 java.base 模塊沒有向未命名模塊開放 java.util 包
解決方法:啟動(dòng) haidnorJVM 時(shí)添加 JVM 參數(shù) --add-opens java.base/java.util=ALL-UNNAMED 繞過訪問性限制
以上就是利用Java編寫一個(gè)Java虛擬機(jī)的詳細(xì)內(nèi)容,更多關(guān)于Java虛擬機(jī)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java實(shí)現(xiàn)文件批量重命名具體實(shí)例
這篇文章主要介紹了Java實(shí)現(xiàn)文件批量重命名具體實(shí)例,需要的朋友可以參考下2014-02-02
使用springmvc臨時(shí)不使用視圖解析器的自動(dòng)添加前后綴
這篇文章主要介紹了使用springmvc臨時(shí)不使用視圖解析器的自動(dòng)添加前后綴,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
Java使用itext5實(shí)現(xiàn)PDF表格文檔導(dǎo)出
這篇文章主要介紹了Java使用itext5實(shí)現(xiàn)PDF表格文檔導(dǎo)出,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01
swing jtextArea滾動(dòng)條和文字縮放效果
這篇文章主要為大家詳細(xì)介紹了swing jtextArea滾動(dòng)條和文字縮放效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
Java調(diào)用接口如何獲取json數(shù)據(jù)解析后保存到數(shù)據(jù)庫
這篇文章主要介紹了Java調(diào)用接口如何獲取json數(shù)據(jù)解析后保存到數(shù)據(jù)庫問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
SpringBoot實(shí)現(xiàn)token登錄的示例代碼
在進(jìn)行登錄驗(yàn)證時(shí),我們需要session或cookie會(huì)話進(jìn)行驗(yàn)證,當(dāng)我們脫離瀏覽器用app等向服務(wù)端發(fā)請(qǐng)求就沒有session和cookie機(jī)制,這時(shí)我們就需要使用token令牌進(jìn)行登錄驗(yàn)證,本文就詳細(xì)的介紹一下,感興趣的可以了解一下2022-03-03

