java 常量折叠_深入理解Java虚拟机之早期编译器优化

Javac编译器

Javac编译器是一个由Java语言编写的程序

Javac的源码与调试

从Sun Javac的代码来看,编译器大致分为3个过程:

解析与填充符号表的过程

插入式注解处理器的注解处理过程

分析与字节码生成的过程

Javac编译动作的入口为com.sun.tools.javac.main.JavaCompiler类,上述3个过程的代码逻辑集中在这个类的compile()和compile2()方法中。

8815afe7e0b8d252c8d7b5d8ace40078.png

解析与填充符号表

解析步骤由上图的parseFiles()方法完成,解析步骤包括了经典程序编译原理中的词法分析和语法分析两个过程。

词法、语法分析

词法分析是将源代码的字符流转变为标记(Token)集合,标记为编译过程的最小元素。关键字、变量名、字面量、运算符都可以成为标记。如int a = 3;,int就是一个Token。词法分析过程由com.sun.tools.javac.parser.Scanner类来实现。

语法分析是根据Token序列构造抽象语法树的过程,抽象语法树是一种用来描述程序代码语法结构的树形表示方式,语法树的每一个节点都代表着程序代码中的一个语法结构。

可以根据Eclipse AST View插件分析出代码的抽象语法树图。在Javac的源码中,语法分析过程由com.sun.tools.javac.parser.Parser类实现,这个阶段产生的抽象语法树由com.sun.tools.javac.tree.JCTree类表示,经过这个步骤后,后续的操作都建立在抽象语法树之上。

填充符号表

完成词法、语法分析之后就是填充符号表的过程,就是图中enterTrees()方法。符号表是由一组符号地址和符号信息构成的表格。符号表中所登记的信息在编译的不同阶段都要用到,在语法分析中,符号表所登记内容将用于语义检查和产生中间代码。在目标代码生成阶段,当对符号名进行地址分配时,符号表是地址分配的依据。

注解处理器

在jdk1.5之后,Java语言提供了对注解(Annotation)的支持。在jdk1.6中,提供了一组插入式注解处理器的标准API在编译期间对注解进行处理,其中,我们可以读取、修改、添加抽象语法树中的任意元素。如果这些插件在处理注解期间对语法树进行修改,编译器将回到解析及填充符号表的过程重新处理,直到所有插入式注解处理器都没有再对语法树进行修改为止,每一次循环称为一个Round,也是上图中的回环过程。

插入式注解处理器的初始化过程是在initPorcessAnnotations()方法中完成的,而它的执行过程则是在processAnnotations()方法中完成的,这个方法判断是否还有新的注解处理器需要执行,如果有的话,通过com.sun.tools.javac.processing.JavacProcessingEnvironment类的doProcessing()方法生成一个新的JavaCompiler对象对编译的后续步骤进行处理。

语义分析与字节码生成

语法树表示一个结构正确的源程序的抽象,但无法保证源程序是符合逻辑的。而语义分析的主要任务是对结构上正确的源程序进行上下文有关性质的审查。

标注检查

语法分析过程分为标注检查以及数据及控制流分析两个步骤,为图中的attribute()和flow()

标注检查步骤检查的内容包括诸如变量的使用前是否已被声明、变量与赋值之间的数据类型是否能够匹配等。

在标注检查步骤中,有一个重要的动作称为常量折叠,如果我们在代码中定义了int a=1+2;那么在语法树上仍能看见字面量“1”,“2”以及操作符“+”,但经过折叠后,将会被折叠为字面量“3”,所以int a=1+2;比起int a=3;并不会增加程序运行期间计算量。

标注检查步骤在Javac源码中的实现类为com.sun.tools.javac.comp.Attr类和com.sun.tools.comp.Check类。

数据及控制流分析

数据及控制流分析是对程序上下文逻辑更进一步的验证,可以检查出诸如程序局部变量在使用前是否有赋值、方法的每条路径是否都有返回值、是否所有的受查异常都被正确处理等问题。

在Javac的源码中,数据及控制流分析的入口为上图中的flow()方法,具体操作由com.sun.tools.javac.comp.Flow类完成。

解语法糖

语法糖指在计算机语言中添加的某种语法,这种语法对语言的的功能并未有影响,但是更加方便程序员使用。

Java中的常用语法糖主要是前面提到过的泛型、变长参数、自动装箱/拆箱等,虚拟机运行时不支持这些语法,它们在编译阶段还原回简单的基础语法结构,这个过程称为解语法糖。

在Javac源码中,解语法糖的过程由desugar()方法触发,在com.sun.tools.javac.comp.TransTypes类和com.sun.tools.javac.comp.Lower类完成。

字节码生成

字节码生成是Javac编译过程的最后一个阶段,在Javac源码里面由com.sun.tools.javac.jvm.Gen类完成。字节码生成阶段不仅仅是把前面的各个步骤所生成的信息转化成字节码写到磁盘中,编译器还进行了少量的代码添加和转换工作。

完成了对语法树的遍历和调整之后,就会把填充了所有所需信息的符号表交给com.sun.tools.javac.jvm.ClassWriter类,由这个类的writeClass()方法输出字节码,生产最终的Class文件,到此为止整个编译过程宣告结束。

Java语法糖

泛型与类型擦除

Java语言中的泛型只在程序源码中存在,在编译后的字节码文件中,就已经替换为原来的原生类型了,并且在相应的地方插入强制转型代码,Java语言中的泛型实现方法称为类型擦除,基于这种方法实现的泛型称为伪泛型。

由于Java泛型的引入,JCP组织引入了诸如Signature、LocalVariableTypeTable等新的属性用于解决伴随泛型而来的参数类型的识别问题。Signature作用就是存储一个方法在字节码层面的特征签名,这个属性中保存的参数类型并不是原生类型,而是包括了参数化类型的信息。

从Singature中可以看出,所谓的擦除,只是对方法的Code属性中的字节码进行擦除,实际上元数据还是保留了泛型信息,这也是我们能通过反射手段取得参数化类型的根本依据。

自动装箱、拆箱与遍历循环

自动装箱、拆箱在编译之后被转化成了对应的包装盒还原方法。遍历循环则把代码还原成了迭代器的实现。变长参数在调用时候变成了一个数组类型的参数。

条件编译

C、C++中是使用预处理器指示符来完成条件编译,而在Java中没有预处理器,因为Java天然的编译方式(编译器并非一个个地编译Java文件,而是将所有编译单元的语法树顶级节点输入到待处理列表后再进行编译,因此各个文件之间能够互相提供符号信息。)无须使用预处理器。

而Java使用条件为常量的if语句来进行条件编译。如果使用常量与其他带有条件判断能力的语句搭配,则可能在控制流分析中提示错误,被拒绝编译。

根据布尔常量值的真假,编译器将会把分支中不成立的代码块消除掉,这一工作将在编译器解除语法糖阶段(com.sun.tool.javac.comp.Lower类中)完成,因为这种条件编译的实现方式使用了if语句,所以只能写在方法体内部,因此它只能实现语句基本块级别的条件编译。

参考《深入理解Java虚拟机》

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

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

相关文章

Spring Data JPA 从入门到精通~JpaRepository介绍

从 JpaRepository 开始的子类,都是 Spring Data 项目对 JPA 实现的封装与扩展。JpaRepository 本身继承 PagingAndSortingRepository 接口,是针对 JPA 技术的接口,提供 flush()、saveAndFlush()、deleteInBatch()、deleteAllInBatch() 等方法…

深入理解程序从编译到运行

From:http://blog.chinaunix.net/uid-22327815-id-3540305.html 从 Hello World 说程序运行机制:http://www.sohu.com/a/132798003_505868 C/C 中如何在 main() 函数之前执行一条语句?:https://www.zhihu.com/question/26031933 …

技术力量 | 社会计算与计算社会: 智慧社会的基础与必然

来源:智慧城市决策参考摘要:基于社会计算,智慧社会可充分利用开放流动的大数据资源, 综合协调人、地、事、物和组织等各种要素, 形成信息对称、权利对等、扁平化组织的社会结构, 推动传统社会管理模式向分布式、集约化、信息化、智能化、全响…

php关闭当前页_php如何直接关闭页面注销SESSION

php如何直接关闭页面注销SESSION发布时间:2020-07-09 09:03:06来源:亿速云阅读:100作者:Leah这篇文章将为大家详细讲解有关php如何直接关闭页面注销SESSION,文章内容质量较高,因此小编分享给大家做个参考&a…

ELF格式文件符号表全解析及readelf命令使用方法

From:http://blog.csdn.net/yasi_xi/article/details/45197583 readelf命令:http://man.linuxde.net/readelf ELF文件格式解析:https://www.2cto.com/kf/201605/511370.html ELF文件格式解析:http://blog.csdn.net/earbao/arti…

Spring Data JPA 从入门到精通~QueryByExampleExecutor的使用

QueryByExampleExecutor 的使用 按示例查询(QBE)是一种用户友好的查询技术,具有简单的接口,它允许动态查询创建,并且不需要编写包含字段名称的查询。从 UML 图中,可以看出继承 JpaRepository 接口后&#…

谷歌大脑提出对智能体进行「正向-反向」强化学习训练,加速训练过程

原文来源:arXiv作者:Ashley D. Edwards、Laura Downs、James C. Davidson「雷克世界」编译:嗯~是阿童木呀、KABUDA、EVA在强化学习问题中,关于任务目标的制定,往往需要开发人员花费很多的精力,在本文中&…

Linux二进制实用工具Binutils工具集解析()

From:http://blog.csdn.net/zqixiao_09/article/details/50783007 GNU Binutils:http://www.gnu.org/software/binutils/ GNU Binutils详解:http://www.crifan.com/files/doc/docbook/binutils_intro/release/html/binutils_intro.html 交叉…

Spring Data JPA 从入门到精通~JpaSpecificationExecutor的使用方法

JpaSpecificationExecutor 源码和 API 我们也可以通过 idea 工具详细看其用法和实现类,JpaSpecificationExecutor 是 Repository 要继承的接口,而 SimpleJpaRepository 是其默认实现。而通过源码来看其提供的 API 比较简单、明了,有如下几个方…

谷歌人工智能野心:从“下围棋”开始走向商用赚钱

来源:腾讯科技作为人工智能研发的领先企业,谷歌已经开始从技术研发走向了产品商用,创造营收成为一个新目标。谷歌在人工智能领域进行研发的时间比较长,陆续收购了优秀的专业公司。鉴于此,谷歌获得了先发优势&#xff0…

Linux异步之信号(signal)机制分析

From:http://www.cnblogs.com/hoys/archive/2012/08/19/2646377.html From:http://kenby.iteye.com/blog/1173862 Linux下的信号详解及捕捉信号:http://www.jb51.net/article/90695.htm linux信号详解:http://blog.csdn.net/f…

DeepMind集成AI智能体架构「MERLIN」:基于目标导向智能体中的无监督预测记忆

来源:arXiv摘要:在自然界中,动物往往会执行目标导向的行为,尽管它们的传感器的范围有限。作者:Greg Wayne、 Chia-Chun Hung、David Amos、Mehdi Mirza、Arun Ahuja、Agnieszka Grabska-Barwinska、Jack Rae、Piotr Mi…

手机反编译java源码,Android反编译(一)之反编译JAVA源码

Android反编译(一)之反编译JAVA源码[目录]1、工具2、反编译步骤步骤1:把apk文件后缀名改为.zip步骤2:解压zip包得到classes.dex步骤3:将Dex反编译为Jar包(工具:dex2jar)命令: CMD>dex2jar.bat classes.dex步骤4:用j…

一文详解计算机视觉的广泛应用:网络压缩、视觉问答、可视化、风格迁移等

作者 | 张皓(南京大学)来源:人工智能头条丨公众号引言深度学习目前已成为发展最快、最令人兴奋的机器学习领域之一,许多卓有建树的论文已经发表,而且已有很多高质量的开源深度学习框架可供使用。然而,论文通…

Linux-进程、进程组、作业、会话、控制终端详解

From:http://www.cnblogs.com/JohnABC/p/4079669.html Linux进程优先级的处理--Linux进程的管理与调度(二十二):http://blog.csdn.net/gatieme/article/details/51719208 进程 、进程组、会话、控制终端之间的关系:http://blog.csdn.net/y…

浅谈项目开发现状(一)

在现在的软件开发中,一些大的软件公司有充分的资金,所以他的公司人员组织架构能组成:需求分析团队(为了更好的了解用户的完整需求)--->研发团队(通过计算机语言来实现用户需求),方…

波士顿咨询:2018最具创新力企业50强

来源:前瞻网在波士顿咨询公司评选的2018年最具创新力公司中,有11家公司——其中包括前10名中的7家——都是“数字原住民”,按定义也就是“数字创新者”。榜单上大多数公司已经将数字技术建立在他们的创新计划中。这一趋势在各个行业都很普遍&…

Linux 线程

Linux 的多线程编程的高效开发经验:https://www.ibm.com/developerworks/cn/linux/l-cn-mthreadps/ linux线程的实现:http://www.cnblogs.com/zhaoyl/p/3620204.html 线程概念经典解析:http://blog.chinaunix.net/uid-29613952-id-421477…

Spring Data JPA 从入门到精通~JpaSpecificationExecutor实现原理

JpaSpecificationExecutor 实现原理 我们还是先通过开发工具,把关键的类添加到Diagram上面进行分析,如图: 我们通过上图可以看一下,前面介绍的几个类之间的关联关系。 SimpleJpaRepository 实现类中的关键源码如下: …

微信发布首份《移动支付时代的无人零售报告》

来源:爱范儿 作者:Panda3 月 30 日,微信支付行业运营总监白振杰在 2018 智慧无人零售大会上发布了《移动支付时代的无人零售行业报告》,报告首次结合中国百货商业协会权威调研和微信支付的数据分析能力,揭示了移动支…