vlambda博客
学习文章列表

Java解释器和编译器

前言

这几天重新研究了一下内存模型、内存屏障,在学习内存屏障的时候,了解了lock前缀指令,为了编译出lock前缀指令,于是去学了一下字节码指令。因为还要添加一些运行参数,于是今天又看了一下午JVM的编译问题。知识一环套一环,现在内存屏障这一块还没搞完。这可能也正是自学乐趣之所在,知识无限延展,层层连贯,于是晚上就整理了一下关于java编译方面总结。

Java代码如何运行

我们写的Java代码是高级语言,机器肯定是读不懂的。所以我们需要将它转换成机器能读懂的机器语言(机器码)。转换工作主要分为以下几个步骤:

前端编译器

javac就是前端编译器,可以将java文件编译成字节码组成的class文件。java代码如下:

public class Info {
public static void main(String[] args) {
int a = 1;
System.out.println(a);
}
}

执行javac Info.java生成Info.class文件,再使用javap -c Info.class来查看其中的字节码。

class中字节码内容如下: 

解释器和即时编译器

我们通过javac将java文件编译成class文件,当jvm启动加载class,需要逐条执行字节码指令来完成程序功能。但是程序的执行还是得在机器上,但是机器是不认识字节码的,所以我们需要将字节码转换成机器码,这样才能让机器执行程序。 什么是机器码? 机器码就是用二进制代码表示的计算机能直接识别和执行的一种机器指令的集合。而解释器和即时编译器(Just In Time Compiler,JIT)就是JVM中将字节码转化为机器码的工具。

解释器

解释器是一行一行地将字节码解析成机器码,解释到哪就执行到哪,狭义地说,就是for循环100次,你就要将循环体中的代码逐行解释执行100次。当程序需要迅速启动和执行时,解释器可以首先发挥作用,省去编译的时间,立即执行。

即时编译器(JIT)

即时编译器按照我的理解就是:以方法为单位,将热点代码的字节码一次性转为机器码,并在本地缓存起来的工具。避免了部分代码被解释器逐行解释执行的效率问题。即时编译器分为两种,Client Compiler(C1编译器)和Server Compiler(C2),默认使用的是C2,因其运行性能更高。

什么是热点代码?

被多次调用的方法和循环体被认定为热点代码。热点代码的判断方法有两种,一是基于采样的热点探测:周期检查每个线程栈顶,统计哪个方法出现次数多,但是不准确;二是基于计数器的热点探测:目前在用,为每个方法建立计数器,统计方法的调用次数。计数器分为方法调用计数器(默认阈值C1是1500次,C2是1w,到达阈值则触发即时编译)和回边计数器(统计一个方法中循环体的执行次数)。下图为方法调用计数器的执行过程: 目前主流的HotSpot虚拟机中默认是采用解释器与其中一个编译器(C2编译器)直接配合的方式将字节码转换成机器码

运行参数

在执行java程序的时候,以下参数是和编译方面的运行及调试参数。

结语

这就是对解释器、编译器简单的介绍,主要是讲述了一下概念性的东西及个人理解。