「JVM 编译优化」javac 编译器源码解读

news2024/12/26 0:11:43

Java 的编译过程

  • 前端编译: 编译器的前端,将 Java 文件转变成 Class 文件的过程;如 JDK 的 javac、Eclipse JDT 中的增量式编译器 ECJ;
  • 即使编译: JIT,Just In Time Compiler,在运行期将字节码转变成本地机器码的过程;如 HotSpot VM 的 C1、C2 编译器,Graal 编译器;
  • 提前编译: AOT,Ahead Of Time Compiler,直接静态将程序编译成目标机器指令集的二进制代码的过程;如 JDK 的 Jaotc、GNU Compiler for the Java(GCJ)、Excelsior JET;

即使编译器在运行期的优化支持了程序执行效率的提升,而前端编译器在编译期的优化支持了程序员的编码效率和语言使用幸福感的提高;

文章目录

      • 1. javac 的源码与调试
      • 2. 解析与填充符号表
      • 3. 注解处理器
      • 4. 语义分析与字节码生成

1. javac 的源码与调试

  • JDK 6 以前,javac 不属于标准 Java SE API,代码独立存放在 tools.jar,使用时需要将路径加入 ClassPath;

  • JDK 6 开始,javac 晋升成标准 Java 类库,源码放在 JDK_SRC_HOME/langtools/src/share/classes/com/sun/tools/javac

  • JDK 9 开始,整个 JDK 的 Java 类库模块化重构,javac 编译器放在 jdk.compiler 模块,存放路径为 JDK_SRC_HOME/src/jdk.compiler/share/classes/com/sun/tools/javac

  • OpenJDK 源码

请添加图片描述

可以直接执行 javac.Main 的 main() 方法来执行编译,参数与直接使用 javac 命令一致;

从 javac 代码的总体结构看,编译过程大致可以分为 1 个准备过程和 3 个处理过程;

  • 准备过程: 初始化插入式注解处理器;
  • 解析与填充符号表过程,包括:
    a. 词法、语法分析;将源代码的字符流转变为标记集合,构造出抽象语法树;
    b. 填充符号表;产生符号地址和符号信息;
  • 插入式注解处理器的注解处理过程: 插入式注解处理器的执行阶段,影响 javac 的编译行为;
  • 分析与字节码生成过程,包括:
    a. 标注检查;对语法的静态信息进行检查;
    b. 数据流及控制流分析;对程序动态运行过程进行检查;
    c. 解语法糖;将简化代码编写的语法糖还原为原有的形式;
    d. 字节码生成;将前面各个步骤所生成的信息转化成字节码;

插入式注解可能会产生新的符号,如果有新的符号产生,就必须转回解析、填充符号表的过程重新处理新的符号;

请添加图片描述

javac 编译入口代码在 com.sun.tools.javac.main.JavaCompiler 类的 compile() 方法;

2. 解析与填充符号表

请添加图片描述

  • parseFiles: 1.1,词法分析、语法分析;
  • enterTrees: 1.2,输入到符号表;
  • processAnnotations: 2,执行注解处理;

a. 词法、语法分析

  • 词法分析,将源代码的字符流转变成标记(Token)集合,字符是程序编写的最小单元,而 Token 是编译的最小单元;关键字、变量名、字面量、运算符等都是 Token,不可再拆分;javac 的词法分析由 com.sun.tools.javac.parser.Scanner 类实现;
  • 语法分析,根据 Token 序列构造抽象语法树(Abstract Syntax Tree,AST,描述一个结构正确的源程序,程序语言结构的树形表示),树的每一个节点代表着程序的一个语法结构(Syntax Construct);包、类型、修饰符、运算符、接口、返回值、代码注释等,都可以是一种特定的语法结构;javac 的语法分析由 com.sun.tools.javac.parser.Parser 类实现,抽象语法树以 com.sun.tools.javac.tree.JCTree 类表示;

b. 填充符号表

  • 符号表(Symbol Table),一组符号地址和符号信息构成的数据结构(包含每个编译单元的抽象语法树的顶级节点和 package-info.java 的顶级节点),类似于 Hash 表的键值对存储结构(也可以是有序符号表、树状符号表、栈结构符号表等形式);符号表在语义分析阶段用于语义检查和产生中间代码,在目标代码生成阶段用于地址分配;javac 中填充符号表由 com.sun.tools.javac.comp.Enter 类实现;

3. 注解处理器

Java 在 JDK 5 开始支持注解(Annotations),原只对程序运行期间发挥作用;到 JDK 6 时添加了插入式注解处理器的标准 API,提前至编译期处理特定注解,可以影响前端编译器的工作过程;

插入式注解处理器相当于编译器的插件,通过这些插件可以读取、修改、添加抽象语法树的任意元素;若插件在处理注解期间对抽象语法树进行修改,编译器将回退至解析和填充符号表的过程,直到插入式注解处理器不再修改抽象语法树;每一次循环称为一个轮次(Round);

编码效率工具 Lombok 通过注解实现自动生成 getter/setter 方法、空置检查、生成受查异常表、生成 equals() 和 hashCode() 等功能,都是依赖插入式注解处理器实现的;

请添加图片描述

  • initProcessAnnotations: 准备过程,初始化插入式注解处理器;
  • processAnnotations: 完成插入式注解执行处理;若有新的注解处理器需要执行,则通过 com.sun.tools.javac.processing.JavacProcessingEnvironment 类的 doProcessing() 生成一个新的 JavaCompiler 对象,进行后续的编译已处理;

4. 语义分析与字节码生成

  • 语义分析,经过语法分析得到的抽象语法树可以表示一个结构正确的源程序,但无法保证源程序的语义符合逻辑;语义分析则是对结构上源程序进行上下文相关性质进行检查(类型检测、控制流检查、数据流检查等);
int a = 1;
boolean b = false;
char c = 2;
int d = a + c;
int d = b + c;
char d = a + c;

所有代码都可以构造正确的抽象语法树,但后两句在 Java 语言中是不符合逻辑的(语义分析异常,与具体的语言和上下文环境相关);

请添加图片描述

  • attribute: 3.1,语义分析的标注检查;
  • flow: 3.2,语义分析的数据及控制流分析;
  • desugar: 3.3,解语法糖;
  • generate: 3.4. 生成字节码;

a. 标注检查

  • 进行如变量使用前是否已被声明、变量与赋值之间的数据类型是否匹配等的检查;
  • 常量折叠(Constant Folding),javac 对源代码做的极少优化之一;
int a = 1 + 2;

在抽象语法树仍然存在字面量 12 和操作符 +,但经过代码折叠,变量的值会被标记为 3;因此在代码里定义 a=1+2a=3 相比,并不会浪费哪怕一个处理器时钟周期的时间;

javac 的标记检查由 com.sun.tools.javac.comp.Attr 类和 com.sun.tools.javac.comp.Check 类实现;

b. 数据及控制流分析

对程序上下文逻辑进行进一步验证,检查如程序局部变量在使用前是否赋值、方法的每个路径是否都有返回值、是否所有受检异常都被正确处理等;与类加载时的数据及控制流分析的目的一直,但校验范围不同;

public void foo(final int arg){
    final int var = 0;
    // do something;
}

public void foo(int arg){
    int var = 0;
    // do something;
}

两种写法经过 javac 编译所得字节码完全一样,可见局部变量是否被 final 修饰对运行期是完全无影响的(不可知的),变量的不可变仅仅有 javac 编译器在编译期保障的;

javac 的数据及控制流分析由 com.sun.tools.javac.comp.Flow 类实现;

c. 解语法通

  • 语法糖,指计算机语言中的某种语法,其对语言的编译结果和功能不会有实际影响(JVM 不能支持这些语法,这些语法最终会被编译成基本语法结构),但却可以更方便编写者实用该语言(减少代码量、增加可读性、减少出错几率);如泛型(C# 的泛型是 CLR 支持的,不属于语法糖)、变长参数、自动装箱拆箱等;
  • 解语法糖,将语法糖编译成原始基本语法结构;

javac 的解语法糖由 com.sun.tools.javac.comp.TransTypes 类和 com.sun.tools.javac.comp.Lower 类实现;

d. 字节码生成

把语法树、符号表转发成字节码指令写到磁盘,并进行少量代码添加和转换工作;

  • 代码添加,如在语法树中添加实例构造器 <init>() 和类构造器 <clinit>();编译器会把语句块(<init>() 的是{}块,<clinit>() 的是static {} 块)、变量初始化(实例变量和类变量)、调用父类的实例构造器(只有 <init>()<clinit>() 中无须调用父类的 <clinit>(),但经常会生成调用 java.lang.Object 的 <init>() 的代码)等操作收敛到 <init>()<clinit>(),并保障一定顺序执行(先父类实例构造器、再初始化变量、最后语句块);
  • 代码转换,如将字符串的加操作替换为 StringBuilder 或 StringBuffer 的 append() 操作;

javac 的字节码生成由 com.sun.tools.javac.jvm.Gen 类实现;将填充了所有信息的符号表输出到 Class 文件由 com.sun.tools.javac.jvm.CLassWriter 类实现;


上一篇:「JVM 原理使用」在远程服务端动态执行临时代码

PS:感谢每一位志同道合者的阅读,欢迎关注、评论、赞!


参考资料:

  • [1]《深入理解 Java 虚拟机》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/351531.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

COSELF 次元秀场伦敦时装周预告 #虚拟时尚

在虚拟世界里的未来服装&#xff0c;能自由变化自己的样貌和服饰。或许未来会作为人类皮肤的第二表征&#xff0c;极大解放人们的精神自由。COSELF 次元秀场 「预告」数字高定系列时间&#xff1a;本月 17 - 21 日地点&#xff1a;当季伦敦时装周「COSELF 次元秀场-数字高定系列…

excel图片技巧:如何为报表配上节日祝福动画

偶尔跳跃一下&#xff0c;改变一下&#xff0c;哪怕被说成是“拍马屁”也行&#xff0c;因为&#xff0c;快乐、传递快乐是一种幸福&#xff0c;是内心本身就有的欲望。提升自己在同事和领导心里的形象只是传递快乐的附加值。圣诞节就快到了&#xff0c;发送报表的时候附带一个…

vue的组件通信

文章目录3. 组件通信3.1 父组件-->子组件3.3组件自定义事件&#xff08;子->父&#xff09;3.4.全部事件总线&#xff08;两代以上&#xff09;3.5消息的订阅与发布3. 组件通信 3.1 父组件–>子组件 <Student name"张三" :age"18"></St…

Java Excel的数据导入导出

引入依赖 <!-- EasyExcel --> <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.7</version> </dependency><!--csv文件操作--> <dependency><groupId>n…

将ChatGPT整合到Word中

引言自ChatGPT出现&#xff0c;各种基于它的软件扩展纷至沓来&#xff0c;目前Word支持ChatGPT的add-in有两款&#xff0c;可以通过:插入->获取加载项->搜索openai查看。其中Ghostwriter从介绍上看功能比较单一&#xff0c;而且软件需要购买&#xff0c;用自己的API-key&…

php5.6.9安装sqlsrv扩展(windows)

报错:Marning: PHP Startup: Unable to load dynamic 1library D:lphpstudy_prolExtensionslphpl(phps.6.9ntslextphp_ pdo_sqlsry 56 nts′找不到指定的模块。in Unknown on line 0 整整搞了一天才终于解决 我用的是phpstudy_pro&#xff08;也就是小皮v8.1版本&#xff09;&…

Twitter多账号想要做到防关联?还是得靠它

接着上一篇Twitter养号攻略的文章&#xff0c;这篇龙哥就来详细讲讲当批量注册和管理Twitter账号时需要怎么防关联。 Twitter作为海外最流行的社交网站之一&#xff0c;它拥有很庞大的用户量&#xff0c;所以很多跨境电商都会通过Twitter来投放广告、推广自己的产品、提高曝光度…

pdf生成为二维码

当今数字时代&#xff0c;人们越来越依赖在线工具来处理各种任务&#xff0c;比如合并、拆分和压缩PDF等。Mai File就是这样一个在线工具&#xff0c;它可以将PDF文件转换成在线链接&#xff0c;方便您和他人轻松地查看和共享文件。 Mai File的使用非常简单&#xff0c;您只需…

从GPT到chatGPT(三):GPT3(二)

GPT3&#xff08;二&#xff09; 前言 因为上一篇文章 从GPT到chatGPT&#xff08;三&#xff09;&#xff1a;GPT3&#xff08;一&#xff09;阅读量挺高&#xff0c;给了我继续更新对该论文进行解读的莫大动力。这篇文章主要讲原论文的第三章&#xff08;Results&#xff0…

Vue2快速入门(三)前端项目架构搭建、Axios、Vue-Router

文章目录VueCli 4.3搭建前端项目架构创建vue项目前端项目目录结构添加axios添加cube-ui依赖创建新目录http客户端Axios什么是Axios&#xff1f;GET请求方式POST请求方式Axios封装通用后端请求API模块Vue-Router开发前端项目路由什么是vue-router&#xff1f;配置项目路由VueCli…

Hive SQL语言:DDL建库、建表

Hive SQL语言&#xff1a;DDL建库、建表 Hive数据模型总览 Hive SQL之数据库与建库 SQL中DDL语法的作用 ⚫ 数据定义语言(Data Definition Language, DDL)&#xff0c;是SQL语言集中对数据库内部的对象结构进行创建&#xff0c;删除&#xff0c;修改等的操作语言&#xff…

《反电信网络诈骗法》实行,Galaxybase图平台成为电信反诈黑科技

电信网络诈骗在当前的数字化生活中始终是一个高频讨论词。 近年来&#xff0c;随着互联网技术发展迅速&#xff0c;线上交易趋于频繁化&#xff0c;以电信网络诈骗为代表的新型网络犯罪行为也变得越来越高发。根据中国信通院《新形势下电信网络诈骗治理研究报告&#xff08;20…

交互式推荐在外卖场景的探索与应用

外卖场景的用户停留时长低于传统电商&#xff0c;对用户实时需求的理解和反馈有更高的要求。针对业务问题&#xff0c;外卖推荐团队从2021年起开始持续投入&#xff0c;最终摸索出了一套适用于外卖场景的交互式推荐架构和策略&#xff0c;并取得了较好的收益。下文将详细介绍外…

论文:拖曳线列阵拖曳噪声抑制的试验研究,阅读笔记

目录摘要目前的噪声来源和抑制的方法1.来源2.抑制方法针对流致振动引起的间接噪声提出解决方法1.结构减振优化设计2.扩大水听器与护套的间距实验1.如何划分对象组2.实验装置实验结果实验组1&#xff1a;结构减振优化设计的效果实验组2&#xff1a;扩大水听器与护套的间距的效果…

跨境电商卖家敦煌、雅虎、乐天、亚马逊测评自养号的重要性!

作为亚马逊、敦煌、乐天、雅虎等跨境的卖家&#xff0c;这两年以来&#xff0c;面对流量越来越贵的现实&#xff0c;卖家需要更加珍惜每次访问listing页面的流量&#xff0c;把转化做好&#xff0c;把流量尽可能转化为更多的订单。 提升转化率的技巧 提升产品转化率&#xff0…

springsecurity最基础的授权过程

​ 在SpringSecurity中&#xff0c;会使用默认的FilterSecurityInterceptor来进行权限校验。在FilterSecurityInterceptor中会从SecurityContextHolder获取其中的Authentication&#xff0c;然后获取其中的权限信息。当前用户是否拥有访问当前资源所需的权限。 ​ 所以我们在项…

【JAVA八股文】JVM虚拟机相关

JVM虚拟机相关1. JVM 内存结构2. JVM 内存参数3. JVM 垃圾回收4. 内存溢出5. 类加载6. 四种引用7. finalize1. JVM 内存结构 结合一段 java 代码的执行理解内存划分 执行 javac 命令编译源代码为字节码执行 java 命令 创建 JVM&#xff0c;调用类加载子系统加载 class&#xf…

使用 ChatGPT 、Stable Diffuison、React和NodeJS构建网站图库

本文译者为360奇舞团前端开发工程师原文标题&#xff1a;Building a website gallery with ChatGPT, Stable Diffusion, React and NodeJS原文作者&#xff1a;Nevo David原文地址&#xff1a;https://javascript.plainenglish.io/building-a-website-gallery-with-chatgpt-sta…

Apollo Planning规划算法仿真调试(15):使用Vscode断点调试apollo的方法更新版

前言 使用Vscode断点调试apollo的方法之前在该专栏写过一篇分享,后台很多粉丝留言希望写的更详细一点,所以更新一版,尽量将配置过程详细描述,并且附上完整的配置文档。 Vscode 作为轻量化的调试工具深受广大开发者的青睐,虽然大家都用它来看新闻逛论坛炒股,但是用它开发…

【郭东白架构课 模块一:生存法则】01|模块导学:是什么在影响架构活动的成败?

你好&#xff0c;我是郭东白。这节课是我们模块一的导入部分&#xff0c;我会先来介绍模块的主要内容&#xff0c;以及为什么我要讲生存法则这个话题。 一名软件架构师要为相对复杂的业务制定&#xff0c;并且引导实施一个结构化的软件方案。这个发现最终方案和推动实施的过程&…