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 09:14:53
如果觉得我的文章对你有用,请随意赞赏
扫一扫支付
上一篇