javac编译器工作原理(2)java语言的编译过程

Javac编译器工作原理(2)Java语言的编译过程

明白了高级语言到低级语言的编译原理,我们来了解一下Javac编译器是怎么把Java语言,编译成JVM字节码

首先我们来了解一下,Javac编译器
Javac编译器–维基百科

javac(发音为“java-see”)是Oracle Corporation的Java Development Kit(JDK)中包含的主要Java编译器。 Martin Odersky实现了GJ编译器,他的实现成为了javac的基础。
编译器接受符合Java语言规范(JLS)的源代码,并生成符合Java虚拟机规范(JVMS)的Java字节码。javac本身是用Java编写的。 编译器也可以以编程方式调用。

简单来讲,就是Javac编译器将符合规范的java语言代码变为符合规范的JVM字节码

实际上,javac在编译的过程中,会对代码进行检查和优化,比如:添加类默认的构造函数,检查变量类型是否匹配,检查变量使用前是否初始化,检查异常以及捕获语句,解除java语法糖等等

例如有这样一段代码

public class SimpleTest {
    public static void main(String[] args) {
        int i;
        int j = i + 10;
    }
}

使用javac编译器进行编译

javac SimpleTest.java

也就是编译失败了,javac编译器则会给你这样的错误提示

这里写图片描述

javac编译过程主要包括以下四步:

词法分析->语法分析->语义分析->字节码生成
这里写图片描述

1. 词法分析

词法分析将java源代码一个字节一个字节的读进来,根据关键字,辨认出符合规范的Token流。
这样说可能有些抽象,其实就是把一段java代码分解,成为一个个单独的词。

public class SimpleTest{
   // 域,方法代码块等等
}

会分解为不同的Token,第一个就是关键字public,对应的Token类为:Token.PUBLIC。同理class关键字对应:Token.ClASS

根据这些Token流,编译器完成了理解Java语言的第一步,就像你听到一句话,我要吃饭,大脑中第一步是把这句话分解为:我,要,吃饭。

2.语法分析

语法分析器会读取Token流,然后生成一棵语法树,类似下图

这里写图片描述

图片来源RednaxelaFX大神的博客文章

而Javac此时生成的是一棵简单的Java语法树,树的节点是全部是com.sun.tools.javac.tree.JCTree的子类
下图就是一棵图像化的java简单语法树
这里写图片描述

3.语义分析

这个阶段就是编译器对代码进行各种检查和优化,而实际操作的对象就是生成java简单语法树的节点。

这个阶段主要使用到类都在com.sun.tools.javac.comp包

javac编译器首先对代码进行一些检查,保证代码符合java语言规范,比如:

com.sun.tools.javac.comp.Check类会检查简单语法树中,变量类型是否正确,方法返回类型是否与接收的引用值是否匹配等等。
com.sun.tools.javac.Resolve类会检查变量、方法或类的访问是否合法,变量是否是静态变量,变量是否初始化等等。

而javac编译器同时对代码进行一些简单地优化,比如:

com.sun.tools.comp.Flow类会去掉无用代码,比如永假的if语句,同事完成变量的自动转换,主要是自动装箱和拆箱等
com.sun.tools.comp.Flow类还会去除java语法糖,比如foreach转化为for循环等

例如有Java代码:

int i = new Integer(1);

javac编译后生产如下字节码(经过反编译得到的):

     0: new           #2                  // class java/lang/Integer
     3: dup
     4: iconst_1
     5: invokespecial #3                  // Method java/lang/Integer."<init>":(I)V
     8: invokevirtual #4                  // Method java/lang/Integer.intValue:()I
    11: istore_1
    12: return

其他代码可以先不用理解,但是看到invokevirtual #4 // Method java/lang/Integer.intValue,说明编译器自动添加代码,调用了Integer.intValue()方法,完成变量自动拆箱,该方法的返回值是基本类型int。

也相当于java代码变为:

    Integer temp = new Integer(1);
    int i = temp.intValue();

当然javac不是使用这种简陋的方法,而是使用了更高效的字节码代替。

4.生成字节码

经过语法分析以后,生成的java简单语法树会被编译器优化为完善的java语法树
这时Javac编译器调用 com.sun.tools.javac.jvm.Gen类遍历语法树
遍历并生成字节码类似下图
这里写图片描述
图片来自RednaxelaFX大神的博客文章

遍历语法树后,获得JVM字节码,这时字节码就可以交给JVM执行了

这里要说明一点,编译得到后缀为class文件中,字节码不止包括方法体中的各种指令,还有诸如常量池,标志位,魔数等等二进制数据

想了解JVM字节码的可以参考下面文章:
如何理解ByteCode、IL、汇编等底层语言与上层语言的对应关系? - RednaxelaFX的回答 - 知乎
https://www.zhihu.com/question/27831730/answer/38266643


参考资料:
《深入分析Java Web技术内幕》–许令波
《深入理解Java虚拟机》–周志明

http://rednaxelafx.iteye.com/blog/492667 – 强烈推荐阅读R大的博客文章
https://www.cnblogs.com/java-zhao/p/5194064.html

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值