JVM从零开始:字节码的编译原理(三)

原文地址:https://www.sudo.ren/article/82

Java之所以能够解决程序的安全性、跨平台移植性问题,主要原因就是Java源代码的编译结果并不是本地指令,而是字节码文件。

开发人员在使用Java语言编写一个Java程序的时候需要遵守Java语法规范,而降源代码编译为字节码的时候有需要符合JVM规范,

简单来说,前端编译器的主要任务就是负责将符合Java语法贵方的Java代码转换为符合JVM规范的字节码文件。JVM并不会与Java

语言“终生绑定”,任何语言编写的程序都可以运行在JVM中,前提是源码的编译结果满足并包含Java虚拟机的内部指令集、符号表、

以及其他的辅助信息,他就是一个有效的字节码文件,就能被虚拟机所识别并装载运行。 

 

编译器简介:

  • Javac.exe:
    Java前端编译器全量编译;
    负责将符合Java语法规范的Java代码转换为符合JVM规范的字节码文件;
    前端编译器并不会直接涉及编译优化等技术,而是将具体细节移交给HotSpot的JIT编译器负责。

  • ECJ:
    Eclipse缺省前端编译器,增量编译,比javac更高效;
    编译的字节码文件与javac一样。


    编译原理:

    词法解析:将java源码中的关键字和标识符转换为符合java语法规范的Token序列,
                        然后按照指定的排序规则进行匹配校验。

    语法解析:将词法解析后的Token序列整合为一颗结构化的抽象语法树。

    语义解析:将抽象语法树补充得更加完善

    生成字节码。


 

词法解析:主要将Java源码中的关键字和标示符等内容转换为符合Java语法规范的Token序列,然后按照指定的顺序规则进行匹配校验,
                    以便为后续的语法解析步骤做准备。

  • Scanner类:按照单个字符的方式读取Java源文件中的关键字和标示符;

  • com.sun.tools.javac.parser.JavacParser类:将Scanner类读取的字符转换为符合Java语法规范的Token序列,
    该类的对象实例由ParserFactory工厂负责创建;

  • JavacParser类的parseCompilationUnit()方法:由该方法负责调用Scanner类的nextToken()方法,
    获取出Token后再按照指定的顺序规则进行匹配校验;

  • Token序列:一组对应源码字符集合的单词序列(一个枚举类型)。

 

词法解析器又是采用什么方式保存源码字符集合与Token之间的对应关系?
答:先将每一个字符集合都转换为一个对应的Name对象,然后再由com.sun.tools.javac.parser.Keywords类负责实际的Token转换任务。

Keywords类会将Token中的所有枚举常量全部都转换为Name对象,然后将其存储在Name对象的内部类Table中,
这样一来源码字符集合与Token之间的对应关系就成功构建起来了。

 

源码字符集合是如何转换成Token的?
答:当词法解析器需要将源码字符集合转换为Token时,就会通过Names类去调用Name类的fromChars()方法获取一个Name对象,

        然后在调用Keywords类的key()方法时传入这个Name对象就可以成功获取出对应的Token,
        这样一来词法解析器即可成功地将源码将字符集合转换为Token。

 

语法解析:词法解析的Token序列并不完善,只是一个对应的单个源码字符集合,此时语法解析的任务就是将这些零散的Token
                    按照指定的Java语法规范整合起来形成一个有机的整体。

  • JCTree类:语法树中的每一个语法节点都会直接或间接地继承了JCTree,

    与语法树中的每一个节点都保持着密不可分的关系。

  • parseCompilationUnit方法:
      1.包含词法解析方法
      2.包含语法解析方法
                调用qualident() 解析package节点
                调用importDeclaration() 解析import节点
                调用classDeclarartion() 解析class节点

 

语意解析:

  • 为没有构造方法的类型添加缺省的无参构造方法;

  • 检查任何类型的变量在使用前是否都已经经历过初始化;

  • 检查变量类型是否与值匹配;

  • 将String类型的常量进行合并处理;

  • 检查代码中的所有操作语句是否可达;

  • 异常检查;

  • 解除Java语法糖。

 

生成字节码:com.sun.tools.javac.jvm.Gen类

  • JVM的架构模型是基于栈的操作,所有操作都是出栈和入栈。

  • 前4个字节:0xCAFEBABE --> magic

  • 5,6字节:次编译版本号

  • 7,8字节:主编译版本号

  • 一组8字节(64位)单位的字节流组成了一个完整的字节码文件。

 

GCJ编译器:将java源码直接编译为本地机器指令。

  • JavaInJava:寄生在宿主机运行字节码文件。
    需要前端编译器编译字节码文件;
    宿主机装载字节码文件到其内部;
    没有JIT编译器,只靠解释器,所以效率低下。

  • Maxine:
    内置JIT编译器;
    编译结果为本地指令;
    不依赖宿主机独立运行。


评论区

山东小木

2019-11-15 15:26

你复制的内容里 修改一下指定的宽度把 页面变形了 (#^.^#)

为道日损

2019-11-15 15:39

@山东小木 咦?你电脑上显示页面变形了吗?为什么我这边没有呀,哈哈哈

为道日损

2019-11-15 15:40

@山东小木 哦哦,发现了,我改一下

cicicv

2019-11-19 18:18

谁能解释

为道日损

2019-11-20 08:37

@cicicv 你好,不明白你说的问题

热门分享

扫码入社