Skip to content

Latest commit

History

54 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Java实现简易JVM

主要模块和样例:

1. 读取并解析class文件,如String、Thread等类(支持jdk8及以下)

部分类可能在demo运行时用到:

  • zvm\bytecode\java\lang\System.class
  • zvm\bytecode\java\io\PrintStream.class
  • zvm\bytecode\java\lang\Thread.class
  • zvm\bytecode\com\zvm\javaclass\integer\Table1.class(注解相关)
2. 取opcode,解释执行程序。循环运算,入栈出栈
  • 执行样例:
publicclassGaussTest {
publicGaussTest() {
}
publicstaticvoidmain(String[] args) {
intsum = 0;
for(inti = 5; i <= 20; i += 10) {
sum += i;
}
System.out.println(sum);
}
}

输出结果:

filepath : GaussTest20
3. 方法调用(静态方法、构造方法、实例方法(支持继承多态))
  • 静态递归方法执行样例(invokestatic):
publicclassFibonacciTest {
publicstaticvoidmain(String[] args) {
longx = fibonacci(8);
System.out.println(x);
}
privatestaticlongfibonacci(longn) {
if (n <= 1) {
returnn;
} else {
returnfibonacci(n - 1) + fibonacci(n - 2);
}
}
}

输出结果:

filepath : FibonacciTest21
  • 构造方法调用(invokespecial)
publicclassFibonacciTest {
publicstaticvoidmain(String[] args) {
longx = fibonacci(8);
System.out.println(x);
}
privatestaticlongfibonacci(longn) {
if (n <= 1) {
returnn;
} else {
returnfibonacci(n - 1) + fibonacci(n - 2);
}
}
}

输出结果:

filepath : FibonacciTest21
  • 调用实例方法,支持继承多态(invokevirtual)
publicclassInvokeVirtualTest {
publicstaticvoidmain(String[] args) {
Vector2Dv2 = newVector2D(2.1, 2.2);
Vector2Dv3 = newVector3D(3.1, 3.2, 3.3);
v2.multiply(2);
v3.multiply(3);
System.out.println(v2.x);
System.out.println(v2.y);
System.out.println(v3.x);
System.out.println(v3.y);
System.out.println(((Vector3D)v3).z);
}
}

输出结果:

filepath : ch07/InvokeVirtualTest4.24.49.39.6000000000000019.899999999999999
4. 数组
  • 一维int数组冒泡排序:
publicclassBubbleSortTest {
publicstaticvoidmain(String[] args) {
int[] arr = {
22, 84, 77, 11, 95, 9, 78, 56,
36, 97, 65, 36, 10, 24 ,92, 48
};
//printArray(arr);bubbleSort(arr);
//System.out.println(123456789);printArray(arr);
}
privatestaticvoidbubbleSort(int[] arr) {
booleanswapped = true;
intj = 0;
inttmp;
while (swapped) {
swapped = false;
j++;
for (inti = 0; i < arr.length - j; i++) {
if (arr[i] > arr[i + 1]) {
tmp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = tmp;
swapped = true;
}
}
}
}
privatestaticvoidprintArray(int[] arr) {
for (inti : arr) {
System.out.println(i);
}
}
}

输出结果:

filepath : ch08/BubbleSortTest910
...
  • 一维double数组冒泡排序
publicclassDoubleBubbleSortTest {
publicstaticvoidmain(String[] args) {
double[] arr = {
22.2, 84.4, 77.5, 11.2, 95.3, 9.2, 78.2, 56.2,
36.1, 97.1, 65.1, 36.1, 10.3, 24.3 ,92.3, 48.3
};
//printArray(arr);bubbleSort(arr);
//System.out.println(123456789);printArray(arr);
}
privatestaticvoidbubbleSort(double[] arr) {
booleanswapped = true;
intj = 0;
doubletmp;
while (swapped) {
swapped = false;
j++;
for (inti = 0; i < arr.length - j; i++) {
if (arr[i] > arr[i + 1]) {
tmp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = tmp;
swapped = true;
}
}
}
}
privatestaticvoidprintArray(double[] arr) {
for (doublei : arr) {
System.out.println(i);
}
}
}

输出结果:

filepath : ch08/DoubleBubbleSortTest9.210.311.222.224.3
...
5. 字符串和字符串数组
  • 字符串加法,涉及类有java/lang/StringBuilder、java/lang/AbstractStringBuilder、java/lang/Math 、java/util/Arrays、java/io/FilterOutputStream、java/io/OutputStream、 java/io/PrintStream、java/lang/String:
publicclassStringBuilderTest {
publicstaticvoidmain(String[] args) {
Stringhello = "hello,";
Stringworld = "world!";
Stringstr = hello + world;
System.out.println(str);
}
}

输出结果:

filepath : ch09/StringBuilderTest总内存:8912分配8完成当前已使用:8总内存:8912分配12完成当前已使用:20
...
hello,world!
...
  • 字符串数组
publicclassArrayDemo {
publicstaticvoidmain(String[] args) {
int[] a1 = newint[10]; // newarrayString[] a2 = newString[10]; // anewarray//int[][] a3 = new int[10][10]; // multianewarrayintx = a1.length; // arraylengtha1[0] = 100; // iastoreinty = a1[0]; // ialoada2[0] = "0abc"; // aastoreStrings = a2[0]; // aaloadSystem.out.println( s);
a2[1] = "1xxxxyyxyy";
a2[2] = "2xxxxyyxyy";
for(inti = 0; i < 3; i++){
System.out.println(a2[i] + " stringbuilderTest");
}
}
}

输出结果:

filepath : ch09/ArrayDemo总内存:8912分配40完成当前已使用:40
...
0abc总内存:8912分配8完成当前已使用:104总内存:8912分配20完成当前已使用:124
...
0abcstringbuilderTest总内存:8912分配8完成当前已使用:364总内存:8912分配32完成当前已使用:396
...
1xxxxyyxyystringbuilderTest总内存:8912分配8完成当前已使用:580总内存:8912分配32完成当前已使用:612
...
2xxxxyyxyystringbuilderTest
...
6. 调用本地方法
  • 只实现了这个方法println,里面调用了arraycopy
publicclassStringBuilderTest {
publicstaticvoidmain(String[] args) {
Stringhello = "hello,";
Stringworld = "world!";
Stringstr = hello + world;
System.out.println(str);
}
}

输出结果:

filepath : ch09/StringBuilderTesthello,world!
7. GC相关:简单实现了标记清除算法
  • 在zvm\src\main\java\com\zvm\memory\JavaHeap.java的HEAP_MAX_SIZE(此例中为32)的大小
publicclassGCTest1 {
privatestaticfinalintSIZE = 3;
publicstaticvoidmain(String[] args){
test0();
test1();
test2();
}
privatestaticvoidtest0() {
/*字符串会创建22 byte + 8byte的数组:8byte:为String对象,22byte为char[11]*///System.out.println("test0 start");int[] arr = newint[SIZE];
for (inti = 0; i < SIZE; i++){
arr[i] = 100 + i;
}
//System.out.println("test0 start");
}
privatestaticvoidtest1() {
//System.out.println("test1 start");int[] arr = newint[SIZE];
for (inti = 0; i < SIZE; i++){
arr[i] = 100 + i;
}
//System.out.println("test1 start");
}
privatestaticvoidtest2() {
//System.out.println("test2 start");int[] arr = newint[SIZE];
for (inti = 0; i < SIZE; i++){
arr[i] = 100 + i;
}
//System.out.println("test2 start");
}
}

输出结果:

filepath : gc/GCTest1总内存:32分配12完成当前已使用:12总内存:32分配12完成当前已使用:24总内存:32已使用24当前需分配12总内存:32回收情况24->0当前需分配12总内存:32分配12完成当前已使用:12

其他 demo

1. 嵌套类
  • 执行样例:
/** 1. 嵌套类: - 静态嵌套类; Classes - 普通内部类(成员内部类) - 局部内部类 - 匿名内部类 https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html */publicclassT0NestedClass {
staticclassStaticClass{
publicStringstaticClassKey = "staticClassVale";
publicvoidtest(){
System.out.println(staticClassKey);
}
}
/** * 普通内部类 */classGenaralClass{
publicStringgenaralClassKey = "genaralClassValue";
publicvoidtest(){
System.out.println(genaralClassKey);
}
}
publicstaticvoidmain(String[] args){
classLocalClass{
publicStringlocalClassKey = "LocalClassValue";
publicvoidtest(){
System.out.println(localClassKey);
}
}
AnonymousClassanonymousClass = newAnonymousClass(){
publicStringanonymousClassKey = "anonymousClassValue";
publicvoidtest(){
System.out.println(anonymousClassKey);
}
};
/*静态类测试*/StaticClassstaticClass = newStaticClass();
staticClass.test();
/*普通内部类测试*/newT0NestedClass().generalClassTest();
/*局部内部类测试*/LocalClasslocalClass = newLocalClass();
localClass.test();
/*匿名内部类测试*/anonymousClass.test();
}
publicvoidgeneralClassTest(){
GenaralClassgenaralClass = newGenaralClass();
genaralClass.test();
}
}
classAnonymousClass{
publicvoidtest() {
}
}

输出结果:

filepath : nestedclass\T0NestedClass总内存:32000分配4完成当前已使用:4
...
staticClassVale总内存:32000分配0完成当前已使用:92总内存:32000分配8完成当前已使用:100总内存:32000分配8完成当前已使用:108总内存:32000分配34完成当前已使用:142genaralClassValue总内存:32000分配4完成当前已使用:146总内存:32000分配8完成当前已使用:154总内存:32000分配30完成当前已使用:184LocalClassValueanonymousClassValue
2. 类的加载、链接、初始化
  • 执行样例:
publicclassT1ClassLink {
publicstaticStringvalue1 = "abc";
publicstaticfinalStringfinalValue = "zvm";
publicstaticString[] arr = newString[]{"arr0", "arr1", "dsafasfsdafd"};
publicstaticfinalString[] finalArr = newString[]{"final-arr0", "final-arr1", "final-dsafasfsdafd"};
publicStringgeneralStr = "generalStr";
publicT1ClassLink() {
}
publicstaticvoidmain(String[] var0) {
System.out.println(value1);
System.out.println("zvm");
System.out.println(arr[0]);
System.out.println(finalArr[0]);
System.out.println((newT1ClassLink()).generalStr);
}
}

输出结果:

filepath : classlink\T1ClassLink总内存:32000分配0完成当前已使用:0
...
abc总内存:32000分配8完成当前已使用:210总内存:32000分配6完成当前已使用:216zvm总内存:32000分配8完成当前已使用:224
...
arr0总内存:32000分配8完成当前已使用:426
...
final-arr0总内存:32000分配4完成当前已使用:624
...
generalStr

计划要实现

  • 反射
  • 多线程
  • 同步

怎么运行

1. 环境
  • 在Windows10,基于jdk8开发
  • 打印调试信息,可能需要maven引入Gson或fastjson
  • 支持解析jdk8及以下版本的类,解释执行demo
  • 经测试,demo支持在linux,jdk8中运行
2. IDEA运行
3. cmd运行
F:\projects\zvm>java -classpath E:\JAVA\Maven\com\alibaba\fastjson\1.2.62\fastjson-1.2.62.jar;E:\JAVA\Maven\com\google\code\gson\gson\2.8.5\gson-2.8.5.jar;F:\projects\zvm\target\classes com.zvm.JavaMain -cp F:\projects\zvm\bytecode gc.GCTest1

运行结果:

目录结构

1. 第一级目录
bytecode\ #编译后的字节文件
javaclass\ #测试demo的源文件
src\ #源代码

注:由jdk1.8.0_45\jre\lib\rt.jar中的java文件夹得到zvm\bytecode\java文件夹

2. 源代码目录
com\zvm
basestruct\ #读取字节码为内存中ClassFile时的基本数据结构
classfile\ #类解析相关
attribute\ #属性表:jdk8中的23种属性
constantpool\ #常量池:jdk8中10种常量类型
ClassFile.java #解析后的class文件
cp_info.java #ClassFile中的常量池表示
field_info.java #ClassFile中的字段表示
IOUtils.java #解析字节码的工具类
method_info.java #ClassFile中的方法表示
ZvmClassLoader.java #待重构
draft\ #草稿,无需理会
gc\
GC.java #GC类,目前只有标记清除算法
instruction\ #指令实现(根据Java虚拟机规范分类建立子目录)
arithmetic\ #运算指令
arithmetic\ #算术运算
bitwise\ #位运算符
logic\ #逻辑运算符
relation\ #关系运算符
unary\ #一元运算符 
controltransfer\ #控制转移 
exception\ #抛出异常
loadandstore\ #加载和存储
constant\ #常量加载至操作数栈
load\ #局部变量表至操作数栈
store\ #操作数栈到局部变量表
methodinvocation\ #方法调用
objectcreatemanipulate\ #对象创建和操作
oprandstack\ #操作数栈操作
synchronization\ #同步
typeconversion\ #类型转换
Opcode.java #指令opcode
Instruction.java #所有指令都implements这个接口
interpreter\  CallSite.java #调用方法时的入口
CodeUtils.java #控制pc的工具类
Descriptor.java #方法调用时,表示返回数据和入参结构
Interpreter.java #取opcode并执行的类
Ref.java #表示methodRef或fieldRef:含类名、描述符、方法名/字段名
jnative\ #实现本地方法
NativeConstant.java #调用相关常量
NativeMethod.java #所有本地方法都需要实现这个接口
NativeUtils.java #本地方法注册调用工具
System.java #实现System类中的本地方法
memory\
ArrayFields.java #保存堆中的数组
JavaHeap.java #表示堆,对象和数组都分配在这
MethodArea.java #方法区
ObjectFields.java #表示堆中的对象
runtime\ #运行时数据
struct\ #一些基本数据结构
JavaClass.java #运行时表示:ClassFile的入口,加一些类的信息
JavaFrame.java #运行时表示:一个方法所用的帧
JThread.java #运行时表示:一个线程(目前未实现多线程)
LocalVars.java #运行时表示:帧中的局部变量表
OperandStack.java #运行时表示:帧中的操作数栈
RunTimeEnv.java #运行时的环境,包括JavaHeap、MethodMrea等
StaticVars.java #JavaClass中的静态字段分配内存
ThreadStack.java #线程栈:运行时,方法调用帧由底至上组成线程栈
Vars.java #供LocalVars、LocalVars、ObjectFields继承使用
utils\
TypeUtils.java #类型转换工具类
Cmd.java #解析命令行
JavaMain.java #启动入口类,含main方法
ZVM.java #表示虚拟机

已实现指令(绝大部分实现了)

1. 加载(load)、存储(store)指令,将数据在局部变量表和操作数栈中来回传输
  • 局部变量表->操作数栈:dload,dload_n; iload,iload_n; lload,lload_n; aload,aload_n
  • 操作数栈->局部变量表:dstore,dstore_n; istore,istore_n; lstore,lstore_n; astore,astore_n
  • 常量到操作数栈: bipush,ldc,ldc_w,ldc2_w,iconst_n
2. 运算指令
  • 加法:iadd,ladd
  • 减法: lsub
  • 乘法: dmul
  • 自增: iinc
  • 比较: lcmp
3. 类型转换指令
  • 待实现
4. 对象创建和操作
  • 创建实例: new
  • 创建数组:anewarray,newarray
  • 访问类或实例字段:getstatic,getfield,putfield
  • 将一个数组元素加载到操作数栈:iaload,laload,faload,daload,aaload
  • 将一个操作数栈的值存储到数组中:iastore, lastore, fastore, dastore, aastore
  • 获得数组的长度:arraylength
  • 检查类实例类型的指令:instanceof、checkcast待实现
5. 操作数栈管理
  • pop, pop2, dup, dup2, dup_x1, dup2_x1, dup_x2, dup2_x2, swap
6. 控制转移
  • 条件分支:ifeq, ifne, iflt, ifle, ifgt, ifge, ifnull, ifnonnull, if_icmpeq, if_icmpne, if_icmplt, if_icmple, if_icmpgt if_icmpge, if_acmpeq, if_acmpne
  • 复合条件分支:tableswitch, lookupswitch待实现
  • 无条件分支: goto, goto_w, jsr, jsr_w, ret.
7. 方法调用和返回
  • invokevirtual: 调用对象实例方法,根据对象实际类型分派
  • invokespecial:特殊处理的实例方法:实例初始化方法,父类方法
  • invokestatic:调用类方法
  • invokeinterface:待实现
  • 返回指令: ireturn(used to return values of type boolean , byte , char , short , or int ), lreturn, freturn, dreturn, and areturn
8. 抛出异常
  • 待实现
9. 同步
  • 待实现

引用和参考

1. 文档、书籍参考
  • Java 虚拟机规范
  • 《自己动手写 Java 虚拟机》
  • Java虚拟机规范(Java se7)中文版
  • Java虚拟机规范(Java se8)中文版
  • 周志明的《深入理解 Java 虚拟机》
2. 代码参考
3. 工具

About

A simple JVM implemented in Java: class-file parsing, bytecode interpretation, method dispatch, and mark-sweep GC (JDK 8 and below)

Topics

Resources

Stars

249 stars

Watchers

10 watching

Forks

Releases

Packages

Used by

Contributors

Languages