【Java JMM】编译和优化

1 前端编译

在 Java 技术下, “编译期” 是一个比较含糊的表述, 因为它可能指的是

  1. 前端编译器 (“编译器的前端” 更准确一些) 把 *.java 文件转变成 *.class 文件的过程
  2. Java 虚拟机的即时编译器 (常称 JIT 编译器, Just In Time Compiler) 运行期把字节码转变成本地机器码的过程
  3. 使用静态的提前编译器 (常称 AOT 编译器, Ahead Of Time Compiler) 直接把程序编译成与目标机器指令集相关的二进制代码的过程

这三者的代表性编译器产品

  1. 前端编译器: JDK 的 Javac, Eclipse JDT 中的增量式编译器 (ECJ)
  2. 即时编译器: HotSpot 虚拟机的 C1, C2 编译器, Graal 编译器
  3. 提前编译器: JKD 的 Jaotc, GNU Compiler for the Java (GCJ), Excelsior JET

这 3 类过程中最符合普通程序员对 Java 程序编译认知的应该是第一类。下面讨论的基本都限制在第一种情况。

1.1 Javac 源码

在 JDK8 中, Javac 的源码主要存放在 $JAVA_HOME/lib/tools.jar, 拷贝一份到一个地方, 解压后, 依次进入 com/sun/tools/javac, 这里就是源
码的地方。当然最简单的方式就是打开一包编译器, 顺便建立一个 Java 项目, 搜索 com.sun.tools.javac.Main 就可以了。

从 Javac 代码的总体结构来看, 编译过程大致可以分为 1 个准备过程和 3 个处理过程, 它们分别如下所示

  1. 准备过程: 初始化插入式注解处理器
  2. 解析与填充符号表过程, 包括

2.1 词法, 语法分析。将源代码的字符流转变为标记集合, 构造出抽象语法树
2.2 填充符号表。产生符号地址和符号信息

  1. 插入式注解处理器的注解处理过程: 插入式注解处理器的执行阶段
  2. 分析与字节码生成过程, 包括

4.1 标注检查。对语法的静态信息进行检查
4.2 数据流及控制流分析。对程序动态运行过程进行检查
4.3 解语法糖。将简化代码编写的语法糖还原为原有的形式
4.4 字节码生成。将前面各个步骤所生成的信息转化成字节码

上述 3 个处理过程里, 执行插入式注解时又可能会产生新的符号, 如果有新的符号产生, 就必须转回到之前的解析, 填充符号表的过程中重新处理这些新符号。
大体的流程如下:
Alt 'Java 处理器处理过程'

代码的入口: com.sun.tools.javac.main.JavaCompiler, 后面整个详细的流程省略。

流程:

  1. 词法分析, 源代码的字符流转变为标记 (Token) 集合, 主要由 com.sun.tools.javac.parser.Scanner 实现

  2. 语法分析, 根据标记序列构造抽象语法树, 主要由 com.sun.tools.javac.parser.Parser, 产生的抽象树为 com.sun.tools.javac.tree.JCTree

  3. 填充符号表, 对符号表进行填充, 主要由 com.sun.tools.javac.parser.Enter

  4. 注解处理器, 通过注解处理器, 可以对抽象语法树中的元素进行读取, 修改, 添加, 在处理注解期间对语法树进行过修改, 编译器将回到解析及填充符号表
    的过程重新处理, 直到所有插入式注解处理器都没有再对语法树进行修改为止。插入式注解处理器的初始主要为 com.sun.tools.javac.main.JavaCompiler.initPorcessAnnotations,
    而执行过程则为 com.sun.tools.javac.main.JavaCompiler.processAnnotions

  5. 语义分析, 是对结构上正确的源程序进行上下文相关性质的检查, 大体可以分为 2 个流程 标注检查 (JavaCompiler.attribute) 和控制流分析 (JavaCompiler.flow)。
    标注检查中, 会对源代码中做一个 “常量折叠 (Constant Folding)” 的优化 (int a = 1 + 4; ==> int a = 5)

  6. 解语法糖, 把语法糖还原回原始的基础语法结构, 由 com.sun.tools.javac.comp.TransTypes 和 com.sun.tools.javac.comp.Lower 完成

  7. 字节码生成, 由 com.sun.tools.javac.jvm.Gen 完成, 把前面各个步骤生成的信息 (语法树, 符号表) 转为字节码, 还会进行少了代码的添加和转换,
    类似于 和 , 将字符串的 + , 替换为 StringBuffer 或者 StringBuilder 的 append 操作

  8. 最终由 com.sun.tools.javac.jvm.ClassWriter 输出字节码, 生成 Class 文件。

2 后端编译

编译器无论在何时, 在何种状态下把 Class 文件转换成与本地基础设施 (硬件指令集, 操作系统) 相关的二进制机器码, 它都可以视为整个编译过程的后端。

2.1 即时编译器

目前主流的两款商用 Java 虚拟机 (HotSpot, OpenJ9) 里, Java 程序最初都是通过解释器 (Interpreter) 进行解释执行的, 当虚拟机发现某个方法或
代码块的运行特别频繁, 就会把这些代码认定为 “热点代码 (Hot Spot Code)”, 为了提高热点代码的执行效率, 在运行时, 虚拟机将会把这些代码编译成本
地机器码, 并以各种手段尽可能地进行代码优化, 运行时完成这个任务的后端编译器被称为即时编译器。

现在主流的 Java 虚拟机内部都同时包含解释器和编译器, 2 者各有好处。
当程序需要迅速启动和执行的时候, 解释器可以首先发挥作用, 省去编译的时间, 立即运行。当程序启动后, 随着时间的推移, 编译器逐渐发挥作用, 把越来
越多的代码编译成本地代码, 这样可以减少解释器的中间损耗, 获得更高的执行效率。
当运行环境的内存资源限制较大时, 使用解释执行可以节约内存, 反之, 可以使用编译执行提高效率。
解释器可以作为编译器激进优化 (不能保证所有情况都正确, 但大多数时候都能提升运行速度的优化) 的后备"逃生门", 通过逆优化退回到解释状态执行。

HotSpot 虚拟机中内置了两个 (或三个) 即时编译器: 客户端编译器 (Client Compiler, 简称 C1), 服务端编译器 (Service Compiler, 简称 C2),
还有 JDK10 出现的长期目标是替代 C2 的 Graal 编译器。

热点代码

  • 被多次调用的方法
  • 被多次执行的循环体

对于这两种情况, 编译的目标对象都是整个方法体, 而不会是单独的循环体。

HotSpot 采用基于计数器的热点探测 (Counter Based Hot Spot Code Detection) 的方式确定方法是否为 “热点代码”。 虚拟机会为每个方法 (甚至是代码块)
建立计数器, 统计方法的执行次数, 如果执行次数超过一定的阈值就认为它是 “热点方法”。

为了实现热点计数, HotSpot 为每个方法准备了两类计数器: 方法调用计数器 (Invocation Counter) 和回边计数器 (Back Edge Counter, “回边” 的
意思就是指在循环边界往回跳转)。调用计数器 + 回边计数器之和超过了阈值 (客户端模式, 默认为 1500 次, 服务端模式: 10000 次), 就会向即时编译器
提交一个该方法的代码编译请求。

2.2 提前编译器

2 种作法:
一条分支是做与传统 C, C++ 编译器类似的, 在程序运行之前把程序代码编译成机器码的静态翻译工作;
另外一条分支是把原本即时编译器在运行时要做的编译工作提前做好并保存下来, 下次运行到这些代码 (譬如公共库代码在被同一台机器其他 Java 进程使用)
时直接把它加载进来使用

3 参考

《深入理解Java虚拟机》- 周志明

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

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

相关文章

心有暖阳,笃定前行,2024考研加油

2024考研学子,所有的付出终有收获,阳光终将穿透阴霾,终将上岸。 当曙光破晓的时候,你可曾记得那些星月为伴,孤独为友,理想为灯来指引前行之路的日子,那些默默扎根的日子终将化作星星在未来闪闪发…

Prometheus-JVM

一. JVM监控 通过 jmx_exporter 启动端口来实现JVM的监控 Github Kubernetes Deployment Java 服务,修改 wget https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/0.19.0/jmx_prometheus_javaagent-0.19.0.jar# 编写配置文件&#xff0…

swing快速入门(二十四)绘画板-可调色

注释很详细,直接上代码 上一篇 Look here~ 听我说完再继续看更容易理解: 如果说用之前的绘图方法写一个绘画板你会怎么做?重绘会让之前的内容消失呀,用各种数据结构记录每个像素点的位置或颜色?嘶,感觉很麻…

如何选择出最适合的backbone模型?图像分类模型性能大摸底

到2023年图像分类backbone模型已经拓展到了几十个系列,而有的新算法还在采样vgg、resnet做backbone,比如2022年提出的GDIP-YOLO还在用VGG16做IA参数预测,那是在浪费计算资源并限制了模型性能的提升,应该将目光放到现在的最新模型中…

Mac使用Vmware Fusion虚拟机配置静态ip地址

一、设置虚拟机的网络为NAT 二、修改虚拟机的网络适配器网络 1、查看虚拟机的网卡 cd /etc/sysconfig/network-scripts#有些系统显示的是ens33,ens160等等 #不同的系统和版本,都会有所不同 #Centos8中默认是ens160,在RedHat/Centos7则为ens33 2、查看网…

绩效面谈-大公司提高绩效的必杀技

绩效面谈是一种人力资源管理工具,旨在评估员工绩效并为其提供反馈。其意义包括: 为提高绩效制定具体的目标和计划。通过与员工讨论绩效表现,管理人员和员工可以确定明确的目标和方向,以实现更高的绩效水平。 帮助员工理解工作环…

企业级“RAS”的数据平台如何炼成?

从“看报表”到“数据分析结果直接投入运营”,数字化正在深入企业经营,数据系统正在成为核心生产系统。相应的,企业对“作业挂了”、“系统崩了”、“算不出来”的容忍度越来越低——只有足够稳定、可靠、专业的数据系统,才能及时…

广州华锐互动:船舶安全事故3D虚拟还原系统模拟海上事故发生,帮助员工提高安全意识

随着科技的不断发展,人们对于安全问题的关注度越来越高。在船舶行业中,由于船舶的特殊性和复杂性,船舶事故的发生往往会造成严重的人员伤亡和财产损失。为此,船舶安全事故3D虚拟还原系统应运而生,为船舶安全管理和培训…

微信开发工具修改编译一直报Cannot read property ‘call‘ of undefined?

我个人的解决方法 更新HbuilderX和微信小程序开发者工具到最新版,微信开发者工具-设置-本地设置-调试基础库也换成最新的3.2.4,打开又报错, 把manifest.json文件内的 “mp-weixin” : {“libVersion”: “latest”}配置上就好了 如果不能解…

朱卫明《酒吧情歌》:独立创作,多元音乐元素融合

朱卫明《酒吧情歌》:独立创作,多元音乐元素融合,成就“明式流行音乐”经典 朱卫明,身为音乐制作人和歌手的他,才华横溢,创作无数。2022年1月31日,他推出了一首全新的单曲《酒吧情歌》。从作曲、…

SpringBoot对接支付宝完成扫码支付

文章目录 1、支付方式选择2、交互流程3、对接准备1)加密解密 签名验签2)沙箱环境3)内网穿透 4、二维码5、下单6、异步通知回调7、查询支付结果8、退款9、通用版SDK 需求:系统A对接支付宝,实现支持用户扫码支付 1、支…

第二证券:降息脚步渐近 银行板块估值望受提振

昨日,A股强势反弹,三大股指早盘探底回升,午后发力走高,深成指涨逾1%,创业板指一度涨超2%;北证50指数大幅回落,一度跌近8%;到收盘,沪指涨0.57%报2918.71点,深成…