java开发C语言编译器: JVM的基本原理

时间:2021-02-15 01:14:54

更详细的讲解和代码调试演示过程,请参看视频
用java开发C语言编译器

如果你对机器学习感兴趣,请参看一下链接:
机器学习:神经网络导论

上一节,我们把C语言编译成了可以被java虚拟机加载执行的java汇编语言。这节,我们就jvm的基本机制进行深入了解,如果不理解java虚拟机的体系结构,那么我们不可能把C语言转换成能顺利在虚拟机上执行的字节码。

把握java虚拟机,必须把握一点,那就是java虚拟机的运行是以栈为基础的。理解了这点,对jvm的理解就掌握了一大半。java程序运行时,当某个函数被调用是,jvm会创建一个执行环境,这个环境叫stack frame, 这个环境有三个需要理解的要点,第一是,stack frame 含有一个执行堆栈,任何指令的执行,都会围绕这个堆栈来进行。第二是局部变量数组,函数的输入参数和局部变量就存储在这个数组中。第三个是指令指针PC,它指向下一条要执行的指令。

举个例子,假设java 代码中有一个函数:

int f(int a, int b) {
return a+b;
}

上面代码编译后,jvm会产生的stack frame 如下:
stack:
local_array: a, b
PC-> 把变量a 从local_array 取出,然后压入堆栈
把变量b 从local_array 取出,然后压入堆栈
把堆栈顶部两个元素弹出后相加,把结果压入堆栈
把堆栈顶部的元素弹出后返回。

假设函数的调用方式为 f(1,2) 那么stack frame 形式如下:
stack:
local_array: 1, 2
PC-> 把变量a 从local_array 取出,然后压入堆栈
把变量b 从local_array 取出,然后压入堆栈
把堆栈顶部两个元素弹出后相加,把结果压入堆栈
把堆栈顶部的元素弹出后返回。

执行第一条语句后,stack frame 如下:
stack: 1
local_array: 2
把变量a 从local_array 取出,然后压入堆栈
PC-> 把变量b 从local_array 取出,然后压入堆栈
把堆栈顶部两个元素弹出后相加,把结果压入堆栈
把堆栈顶部的元素弹出后返回。

继续执行当前PC指针指向的语句后,情况如下:
stack: 2, 1
local_array:
把变量a 从local_array 取出,然后压入堆栈
把变量b 从local_array 取出,然后压入堆栈
PC-> 把堆栈顶部两个元素弹出后相加,把结果压入堆栈
把堆栈顶部的元素弹出后返回。

继续执行后情况如下:
stack: 3
local_array:
把变量a 从local_array 取出,然后压入堆栈
把变量b 从local_array 取出,然后压入堆栈
把堆栈顶部两个元素弹出后相加,把结果压入堆栈
PC-> 把堆栈顶部的元素弹出后返回。

依次类推。

你可以看到,java指令运行时,必须确保指令需要的参数全部放在栈上,指令执行后如果有结果,结果也同样会存在在堆栈上。

有了上面的基本理论,我们可以看看上一节程序生成的java汇编代码:

.class public CSourceToJava
.super java/lang/Object

.method public static main([Ljava/lang/String;)V
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "Hello java virtual machine!"
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
return
.end method
.end class

指令getstatic 的作用就是把out对象压入前面所说的堆栈中。ldc 的作用是把字符串压入到前面所说的堆栈中,这两句执行后,堆栈情况如下:
stack:
“Hello java virtual machine!”
out

invokevirtual 指令执行时,它会从堆栈顶部取出两个元素,接着从第二个元素,也就是out对象中,找到要调用的方法,也就是println, 然后把堆栈第一个元素作为参数,调用println方法,执行完毕后,如果有返回值,则把返回值给压入堆栈。

理解了指令的执行原理后,其他语句完全可以跟下面的代码对应起来:

public class CSourceToJava {
public static void main(String[] args) {
System.out.println("Hello java virtual machine!");
}
}

由此语句.class public CSourceToJava对应的就是java代码中类的声明部分:
public class CSourceToJava, 指令.method public static main 对应的就是静态函数main 的声明:public static void main。

语句Ljava/io/PrintStream; 表示对象的类型,例如对象out 的类型是java.io.PrintStream。前面的L表示该对象是一个类,如果前面还有一个左中括号,那表示该对象是一个数组,例如[Ljava/lang/String; 表示的是String[].

更详实讲解请参看视频。

更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号:
java开发C语言编译器: JVM的基本原理