当前位置: 首页 > news >正文

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

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 虚拟机》

相关文章:

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

Java 的编译过程 前端编译: 编译器的前端&#xff0c;将 Java 文件转变成 Class 文件的过程&#xff1b;如 JDK 的 javac、Eclipse JDT 中的增量式编译器 ECJ&#xff1b;即使编译: JIT&#xff0c;Just In Time Compiler&#xff0c;在运行期将字节码转变成本地机器码的过程&…...

Leetcode DAY 34: K次取反后最大化的数组和 and 加油站 and 分发糖果

1005.K次取反后最大化的数组和 class Solution:def largestSumAfterKNegations(self, nums: List[int], k: int) -> int:nums sorted(nums, key abs, reverse True)for i in range(len(nums)):if nums[i] < 0:nums[i] -nums[i]k - 1else:continueif k 0:return sum(…...

2023美赛A题思路

在线解析 https://kdocs.cn/l/ccNGjN9sGugL​kdocs.cn/l/ccNGjN9sGugL A题思路&#xff1a;&#xff08;具体以题目解决问题顺序为主&#xff09; 这道题分析植被就行&#xff0c;主要涉及不同植被间的相互作用&#xff0c;有竞争有相互促进&#xff0c;我查了下“植物科学数…...

前端上传文件

前言 以 vue 举例&#xff0c;原生 html css js 现在应该很少有人去写了 一、绘制样式 绘制两个标签&#xff0c;一个 <div></div> &#xff0c;一个 <input type"file" />&#xff1b; 为 <div></div>添加 css 样式&#xff0c…...

后台管理系统中选项卡的动态渲染

动态渲染选项卡其中router-link是为了当点击选项卡时跳转到选项卡所在的列表选项卡需要动态渲染&#xff0c;其中active是当选中后激活选中的样式为图标添加点击删除事件在状态机配置tabMenu&#xff08;为了动态渲染&#xff09;需要在tabMenu添加&#xff1a;active、title、…...

网络层重点协议之IP协议(IPv4)

网络层的作用就是来路由的选择&#xff0c;规划传输的路径&#xff0c;其中网络层的重点协议就是IP协议。4位版本号版本号的取值只有4和64位首部长度描述了IP报头有多长&#xff0c;报头中有一个选项部分&#xff0c;是变长的&#xff0c;是可有可无的部分&#xff0c;所以IP报…...

CentOS Stream 8配置DNS

1&#xff1a;用CentOS搭建DNS的目的是想解析一台下载服务器&#xff0c;IP地址172.18.0.58&#xff0c;现在是用IP地址方的式访问&#xff0c;想搭建DNS服务器用域名的方式访问。 使用下面的命令查看一下当前系统的Bind版本。 yum info bind 版本是9.11.36.我的CentOS是最小…...

【roLabelImg】windows下旋转框标注软件安装、使用、rolabelimg打包成exe

主要参考&#xff1a; roLabelImg安装、使用、数据格式roLabelImg在Win10系统下打包成exe - 问雪的文章 - 知乎 一、安装 1.1 直接下载exe运行 劝大家直接去下别人编译好的吧&#xff0c;本来是训练模型标记的&#xff0c;结果搞了半天去了解这个软件了&#xff0c;哎~ 我…...

2023美赛F题:绿色经济

文章目录背景要求词汇表背景 国内生产总值&#xff08;GDP&#xff09;可以说是最知名且最常用的衡量一个国家经济健康的指标之一。它通常用于确定一个国家的购买力和贷款能力&#xff0c;为国家提出提高GDP的政策和项目提供了动力。GDP “衡量一个国家在一段特定时间内生产的…...

华为OD机试 - 剩余可用字符集 | 备考思路,刷题要点,答疑 【新解法】

最近更新的博客 【新解法】华为OD机试 - 关联子串 | 备考思路,刷题要点,答疑,od Base 提供【新解法】华为OD机试 - 停车场最大距离 | 备考思路,刷题要点,答疑,od Base 提供【新解法】华为OD机试 - 任务调度 | 备考思路,刷题要点,答疑,od Base 提供【新解法】华为OD机试…...

“笨办法”学Python 3 ——练习 40. 模块、类和对象

练习40 模块、类和对象 知识点&#xff1a; 40.1.0 模块就像字典 my_stuff[apple] #my_stuff是字典&#xff0c;访问字典apple键的值 import mystuff mystuff.apple() #mystuff是模块&#xff0c;模块访问函数apple() print(mystuff.tangerine) #模块访问变量tangerine说明P…...

自动驾驶:BEVDet

自动驾驶&#xff1a;BEVDetIntroductionMethodoloData AugmentationNetwork StructureScale-NMS实验Introduction 作者通过现有的算法&#xff08;LSS&#xff09;、独特的数据增强方案与新的NMS方案整合了一个BEV框架&#xff08;BEVDet&#xff09;。 如下图&#xff1a; …...

vue的组件通信

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

Typescript的定义及使用优势

编程语言的类型&#xff1a; 动态类型语言 (Dynamically Typed Language&#xff09;静态类型语言 (Statically Typed Language&#xff09; 两种语言的含义及区别&#xff1a; 比如JS、python就是动态类型语言&#xff0c;什么是动态类型语言&#xff0c;通俗的讲&#xff0…...

正则验证:手机号码验证

<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title></title> </head> <body> 手机号码<input type"text" id"phone"> <span…...

视频融合 flv流格式对接(上)

FLV 是FLASH VIDEO的简称&#xff0c;FLV流媒体格式是随着Flash MX的推出发展而来的视频格式。由于它形成的文件极小、加载速度极快&#xff0c;使得网络观看视频文件成为可能&#xff0c;它的出现有效地解决了视频文件导入Flash后&#xff0c;使导出的SWF文件体积庞大&#xf…...

提问:影视剪辑解说都是怎样配音的,软件合成还是自己配音?

“影视剪辑解说都是怎样配音的&#xff0c;软件合成还是自己配音&#xff1f;”这是一个很好的问题并且困扰着很多人&#xff0c;因为不知道该如何选择。究竟应该使用软件来完成配音工作呢?还是自己动手配音呢&#xff1f;这是一个很难回答的问题。如果你问我的话&#xff0c;…...

基于RK3588的嵌入式linux系统开发(二)——uboot源码移植及编译

由于官方的SDK占用空间较大&#xff08;大约20GB左右&#xff09;&#xff0c;需要联系相关供应商提供&#xff0c;且官方的SDK通过各种脚本文件进行集成编译&#xff0c;难以理解系统开发的详细过程。本章介绍直接从官方Github网站下载源码进行移植&#xff0c;进行uboot移植及…...

excel报表技巧:几个关于汇报演示方面的小功能

年终了&#xff0c;总结汇报避免不了。如果你的PPT还不够好&#xff0c;那就直接用Excel做汇报吧~这里有5条小技巧&#xff0c;可以帮助你最高效地展示自己的成绩报表&#xff01;想象一下&#xff0c;用SHIFTCTRLF1全屏显示你的工作表&#xff0c;配合上CtrlPageDown进行工作表…...

【数据结构与算法】Manacher算法

&#x1f320;作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《数据结构与算法要啸着学》 &#x1f387;座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;…...

conda相比python好处

Conda 作为 Python 的环境和包管理工具&#xff0c;相比原生 Python 生态&#xff08;如 pip 虚拟环境&#xff09;有许多独特优势&#xff0c;尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处&#xff1a; 一、一站式环境管理&#xff1a…...

树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频

使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

质量体系的重要

质量体系是为确保产品、服务或过程质量满足规定要求&#xff0c;由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面&#xff1a; &#x1f3db;️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限&#xff0c;形成层级清晰的管理网络&#xf…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句&#xff0c;它能够让用户直接在浏览器内练习SQL的语法&#xff0c;不需要安装任何软件。 链接如下&#xff1a; sqliteviz 注意&#xff1a; 在转写SQL语法时&#xff0c;关键字之间有一个特定的顺序&#xff0c;这个顺序会影响到…...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例&#xff0c;模拟20个网页的爬取&#xff0c;每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程&#xff1a;允许程序同时执行多个任务&#xff0c;提高IO密集型任务&#xff08;如网络请求&#xff09;的效率…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案

JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停​​ 1. ​​安全点(Safepoint)阻塞​​ ​​现象​​:JVM暂停但无GC日志,日志显示No GCs detected。​​原因​​:JVM等待所有线程进入安全点(如…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...

MySQL 主从同步异常处理

阅读原文&#xff1a;https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主&#xff0c;遇到的这个错误&#xff1a; Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一&#xff0c;通常表示&#xff…...