Java源码的前端编译


欢迎来到我的博客:TWind的博客

我的CSDN::Thanwind-CSDN博客

我的掘金:Thanwinde 的个人主页


0.前言

当一份Java代码写好时,将其进行编译,运行,并不是简单把这个Java源码从头到尾执行,一般来说会经历前端编译和后端编译两个阶段,前端编译会把Java源码进行分析,拆解,填充并进行一些优化来变成字节码

后端编译是发生在JVM已经在解释执行字节码时的JIT,会进行一些分析来将热点代码直接替换成大部分情况下效率更高的本地机器码,并进行许多优化,从而提高效率

除去前后编译,还有提前编译,后面都会写文章一一介绍


1.前端编译阶段

前端编译是一个Java源码的开始,在这里会将其转化为可以执行(效率不谈)的字节码

Javac

Javac是收录于JDK中的Java编译器。该工具可以将后缀名为.java的源文件编译为后缀名为.class的可以运行于JVM的字节码,Javac是由Java写的,运行javac的实质便是命令行的调用:javac hello.java

Java对于如何把.java文件转化为.class文件规范得很宽松,可能会导致class在一些JDK上能编译,一些却不行的情况

具体来说,Javac的编译过程可分为四个阶段

  • 初始化插入式注解处理器
  • 解析与填充符号表
    • 把源代码转化为标记集合来构造抽象语法树
    • 填充符号表
  • 注解处理器处理注解
  • 语义分析与生成字节码
    • 标注检查
    • 数据流与控制流分析
    • 解语法糖
    • 字节码生成

下面,让我们一个个来看:


初始化插入式注解处理器

JDK5之后,Java提供了对注解的支持,原本的方案是把注解作为运行时才发挥作用的,但是到了JDK6,引入了插入式注解处理器:让注解处理可以发生在编译阶段,一般来说,运行时的注解只能完成一些反射,自动化类的操作,但注解处理时可以动态的向其中加入代码:譬如lombok之类的动态生成代码,使注解能在代码层面影响代码

SPI

许多的框架其实都是依靠于这套机制完成的,而这些是基于一种名为 SPI 的技术

和SPI对应的是API,API(Application Programming Interface)

在这里插入图片描述

API就是上方:实现方已经实现了接口,要求调用方去实现它

SPI反过来:接口由调用方决定,实现方要根据调用方的接口要求去实现这个接口来提供服务

API最典型的实现就是OAuth2服务:你需要按照OAuth2提供商的要求去实现登录过程

SPI最典型的也就是JDBC,日志:各种数据库的JDBC驱动都是按照统一的接口规范来编写的,完全符合调用者制定的规范,众多的日志实现类也同理

但这只是SPI的概念,和Java中修改源代码的需求好像并无交集

具体来说,Java设计了许多接口,来供其他包作者去适配

这其中就包括注解的解释接口:javax.annotation.processing.AbstractProcessor,或是javax.annotation.processing.Processor(插入式注解处理器会放在META-INF/services之中)

在编译的时候,也就是一开始编译,Javac会先加载所有实现了这个接口的类,也就是所有插入式注解处理器(包括你自定义的),这实际上破坏了双亲委派模型

在这里插入图片描述

因为对于javac(对一般的java程序也一样)来说,这些插入式注解处理的实现(SPI)都属于“应用程序级”的,应该由最低级的应用程序类加载器加载

但实际上,他们的接口都位于“高层”,其接口是由启动类加载器加载的,但是这些高层的接口要去加载自己的实现类,就属于高级类加载器加载低级类加载器了

这里,java引入了线程上下文类加载器,这个加载器可以跨界进行加载,这样就能帮助高层的加载器加载底层的类

至于这里为什么强调“线程上下文”,我们可以以tomcat举例:通常里面有多个线程来处理请求,相互应该隔离:对于java,不同类加载器加载的同一个类,视为不同类,不相同也不兼容,这样就能隔离开不同线程

也不用担心每个线程都要去加载一个SPI,只会进行一次类加载,剩下的都会从缓存之中直接加载,尽管是其他线程

总而言之

通过SPI,我们就能解决Javac在准备编译时去加载插入式注解处理器的问题了


解析与填充符号表

词法语法分析

这里会把代码中的字符流转化为标记(Token),Token是编译时的最小元素,比如int a = b + 2,会拆分成6个Token:int ,a,=,b,+,2

接着会把这些Token按照顺序构造成抽象语法树(AST),其代表了一个程序的语法结构,你可以理解为是一个能保存一个程序的所有信息的数据结构

之后,Javac就不会再操作字符流了,一切都会围绕着语法树来进行

填充符号表

符号表类似于一个哈希表,用来标识每一个符号的地址和其信息,用人话来说的话,就是会标记一些Token,记录下这些Token的名字,类型,作用域等等,譬如方法名,变量名这类的,会用来进行各种检测,优化:比如语法检查,分配地址子类的,符号表就像是电话簿一样的角色


注解处理器处理注解

这里相对的简单:会调用每一个注解处理器进行处理,如果其对语法树进行了更改:就会回退到解析与填充符号表 重新处理,因为可能改变了语法以及符号表,这个操作称之为轮(Round),可以抽象成下面这个图

在这里插入图片描述

当所有注解处理器都处理完成后,便会进入下一个阶段:


语义分析与生成字节码

这里已经具有了一个完整的语法树和符号表,最后一步就是把这些转化成字节码了

标注检查

这里会进行类型赋值是否互相兼容,变量使用前是否声明,还会进行常量折叠,这是前端编译中为数不多的优化:int a = 1 + 2,这里会把1 + 2 在语法树上直接变成3

数据及控制流分析

这里会对各种逻辑进行进一步的验证,比如,对于局部变量用之前有无赋值,是否有返回值,异常是否会处理这种更复杂的控制

值得注意的是,这里的分析是和类加载期间的分析(运行时)基本相同,但存在一些东西只能在编译期或者运行时检测

就比如说局部变量的final,JVM对于字节码的要求是越短越好,对于局部变量的final关键字会直接被消除,那如何保证其值不会变?那就轮到编译器来判断了

为什么不会去掉成员变量的final?因为JVM 运行时有可能用到(比如常量折叠、只读约束等)

解语法糖

语法糖是一些用来帮助程序员进行编程的特殊语法,或许其不严谨或不规范但是其能大幅度提升程序员的幸福度

包括泛型(是的,Java匪夷所思没有真正的实现泛型,详见类型擦除),自动拆装箱,自动变长参数等,这些语法会在这个阶段被替换成最基础的语法

字节码生成

这里是最后一个阶段:把语法树,符号表转化成字节码

字节码的格式极其严格,这里会把其严格的转化成字节码,并向其中添加一些其他的代码,比如类构造器,实例构造器

变量初始化,调用父类构造等等

并还会添加一些优化,比如把字符串的+替换成StringBuffer的append之类的

到此,编译结束


现在已经生成了一份详尽且严谨的字节码,接下来一步就是开始解释执行,并开始最大的舞台:后端编译及优化

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JWT6报错误 kid empty unable to lookup correct key

JWT5和jwt6在加密和解密和时候还明些区别的 ,在5中,是不需要这个kid的,加解都不需要。但6中是需要这个keyId。 所以在使用的时候要做个区别,参考下面链接: ThinkPhp5.0.24 JWT报错 ‘“kid“ empty, unable to lookup…

高效学习之一篇搞定分布式管理系统Git !

一、Git是什么1.Git是目前世界上最先进的分布式版本管理系统 2.工作原理/流程workspace:工作区 Index/Stage:暂存区 Repository:仓库区(本地仓库) Remote:远程仓库二、SVN和Git的最主…

AdsPower API 新增查询环境 Cookies 接口,自动化更进一步!

你是不是有过这样的经历?账号在 AdsPower 环境中已经成功登录,但你还要花时间手动导出 Cookies、再整理处理,过程繁琐、效率低下。 现在,我们上线了 API 查询环境 Cookies 的接口,支持通过 API 直接获取已登录环境的 …

Craftium游戏引擎中的客户端同步机制解析

Craftium游戏引擎中的客户端同步机制解析 craftium A framework for creating rich, 3D, Minecraft-like single and multi-agent environments for AI research based on Minetest 项目地址: https://gitcode.com/gh_mirrors/cr/craftium 游戏状态同步的核心问题 在分…

spring cloud负载均衡之FeignBlockingLoadBalancerClient、BlockingLoadBalancerClient

本文主要分析被 FeignClient 注解的接口类请求过程中负载均衡逻辑&#xff0c;流程分析使用的源码版本信息如下&#xff1a;<spring-boot.version>3.2.1</spring-boot.version><spring-cloud.version>2023.0.0</spring-cloud.version>背景 平常我们代码…

提示工程(Prompt Engineering)研究进展

提示工程(Prompt Engineering)研究进展 以及它如何帮助大语言模型(LLMs)和视觉语言模型(VLMs)更好地工作。用简单的话说,就是通过设计巧妙的“提示”(比如指令、例子),让模型在不修改内部参数的情况下,更精准地完成各种任务,比如回答问题、推理、生成内容等。 文档…

【ARM】AI开发板A7处理器JTAG实现指南

一、文档背景尽管开发板原厂提供了相关文档&#xff0c;但可能缺乏对 A7 处理器 JTAG 功能的详细说明。这可能会导致以下问题&#xff1a;开发人员难以理解和利用 A7 处理器的基本功能&#xff0c;阻碍调试和开发进度。在进行Uboot移植过程中&#xff0c;无法应用图形界面的调试…

FPGA(一)Quartus II 13.1及modelsim与modelsim-altera安装教程及可能遇到的相关问题

零.前言 在学习FPGA课程时&#xff0c;感觉学校机房电脑用起来不是很方便&#xff0c;想着在自己电脑上下载一个Quartus II 来进行 基于 vhdl 语言的FPGA开发。原以为是一件很简单的事情&#xff0c;没想到搜了全网文章发现几乎没有一个完整且详细的流程教学安装&#xff08;也…

软考(软件设计师)存储管理—存储空间管理,文件共享保护

一、文件存取方法 1. 顺序存取&#xff08;Sequential Access&#xff09; 原理&#xff1a;按记录写入顺序依次访问特点&#xff1a; 读操作&#xff1a;读取当前位置&#xff0c;指针自动前移写操作&#xff1a;追加到文件末尾 适用场景&#xff1a;磁带设备、日志文件 #merm…

Thinkphp6中如何将macro方法集成到Request类中

在学习crmeb的时候发现他使用了一个macro的方法用在中间件中&#xff0c;于对macro进行了简单的研究&#xff0c;发现这个方法可以在中间件中进行定义一些方法&#xff0c;然后让后面的控制器进行使用。 如&#xff1a; 在授权的中间件中&#xff0c;定义了$request->macro…

Java List 使用详解:从入门到精通

一、List 基础概念1.1 什么是 List&#xff1f;List 就像是一个智能书架&#xff1a;可以按顺序存放书籍&#xff08;元素&#xff09;每本书都有固定位置&#xff08;索引&#xff09;可以随时添加、取出或重新排列书籍// 创建一个书架&#xff08;List&#xff09; List<S…

Java零基础笔记06(数组:一维数组、二维数组)

明确: 程序是用来处理数据的, 因此要掌握数据处理的数据结构数组是编程中常用的数据结构之一&#xff0c;用于存储一系列相同类型的元素。在Java中&#xff0c;数组是一种对象&#xff0c;可以存储固定大小的相同类型元素的集合。1.一维数组数组是一个数据容器,可用来存储一批同…

10倍处理效率提升!阿里云大数据AI平台发布智能驾驶数据预处理解决方案

阿里云大数据AI平台重磅发布智能驾驶数据预处理解决方案&#xff0c;可帮助汽车行业客户实现构建高效、稳定的数据预处理产线流程&#xff0c;数据包处理效率相比自建可提升10倍以上&#xff0c;数据处理推理任务优化提速1倍以上&#xff0c;相同资源产能提升1倍[1]&#xff0c…

SAP HANA内存数据库解析:特性、优势与应用场景 | 技术指南

SAP HANA 是一款列式内存关系数据库&#xff0c;集 OLAP 和 OLTP 操作于一体。相较于同类产品&#xff0c;SAP HANA 需要的磁盘空间更少&#xff0c;并且可扩展性高。SAP HANA 可以部署在本地、公有云或私有云以及混合场景中。该数据库适用于各种数据类型的高级分析和事务处理。…

Openharmony4.0 rk3566上面rknn的完美调用

一 背景&#xff1a; 我们都知道如果要在android上面使用rknn推理模型需要按照如下的步骤&#xff1a; 详细请参考笔者的文章&#xff1a;Android11-rk3566平台上采用NCNN&#xff0c;RKNN框架推理yolo11官方模型的具体步骤以及性能比较-CSDN博客 简而言之就是 模型转换&#…

Java多线程知识小结:Synchronized

在Java中&#xff0c;synchronized 关键字是实现线程同步的核心工具&#xff0c;用于保证同一时刻只有一个线程可以执行被修饰的代码块或方法。以下从基本原理、锁升级过程、应用场景及优化建议四个维度详细解析&#xff1a; 一、基本原理 1. 同步的对象 synchronized 锁的是对…

MTK项目wifi.cfg文件如何配置的Tput和功耗参数

下面的MTK参数主要与无线网络(Wi-Fi)配置相关,特别是与WMM(Wi-Fi Multimedia)和功率控制相关的设置 WMM相关参数: WmmParamCwMax/WmmParamCwMin:定义竞争窗口的最大/最小值,这里设置为10/4,用于控制信道访问的退避机制13 WmmParamAifsN:仲裁帧间间隔数,设置为3影响不同…

分水岭算法:图像分割的浸水原理

分水岭算法&#xff1a;基于拓扑地貌的边界提取核心原理 分水岭算法将图像视为拓扑地貌&#xff0c;灰度值代表海拔高度。通过模拟浸水过程&#xff1a;局部极小值&#xff1a;对应集水盆&#xff08;区域内部&#xff09;。分水岭线&#xff1a;集水盆之间的山脊&#xff08;区…

汽车功能安全系统阶段开发【技术安全方案TSC以及安全分析】5

文章目录1 技术安全方案 (Technical Safety Concept - TSC)2 系统安全架构设计 (System Safety Architecture Design)3 如何进行安全分析 (Safety Analysis)4 技术安全需求 (TSR) 如何分配到系统架构1 技术安全方案 (Technical Safety Concept - TSC) 技术安全方案 (Technical…

学习软件测试的第十二天(接口测试)

一.如果一个接口请求不通&#xff0c;那么你会考虑那些方面的问题&#xff1f;如果一个接口请求不通&#xff0c;我会像“排查水管漏水”一样一步步定位问题发生在哪一段&#xff0c;主要从这几个方向去思考&#xff1a;当一个接口请求不通时&#xff0c;我会从以下几个方面进行…