javac源码编译-javac编译器的工作原理(二)java语言的编译过程

Javac编译器工作原理(二)Java语言编译流程

了解了从中级语言到低级语言的编译原理之后,我们来了解一下Javac编译器是如何将Java语言编译成JVM字节码的

首先我们看一下Javac编译器

Javac 编译器 - 维基百科

javac(发音为“java-see”)是 Oracle 公司 Java 开发工具包 (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语句javac源码编译javac源码编译,学生将完成变量的手动转换,主要是手动装箱和拆箱等。

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."":(I)V
     8: invokevirtual #4                  // Method java/lang/Integer.intValue:()I
    11: istore_1
    12: return

其他代码不用先了解,看invokevirtual#4//Methodjava/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 的回答 - 知乎

参考:

《深入解析JavaWeb技术内幕》——徐凌波

《深入理解Java虚拟机》——周志明

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悟空资源网 源码编译 javac源码编译-javac编译器的工作原理(二)java语言的编译过程 https://www.wkzy.net/game/150133.html

常见问题

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务