【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.mzph.cn/news/239237.shtml

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

相关文章

【华为鸿蒙系统学习】- HarmonyOS4.0之App项目开发|自学篇

🌈个人主页: Aileen_0v0 🔥热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 💫个人格言:"没有罗马,那就自己创造罗马~" 目录 创建鸿蒙第一个App项目 项目创建 工程目录区 预览区 运行Hello World 基本工程目录 ws:工…

ROS-小海龟案例---ROS命令

ROS-小海龟案例—ROS命令 rosnoderos:节点管理命令,通过此命令可以查看、操作以及监测已经运行的ros节点: rosnode 命令 作用 rosnode list 列出当前运行的node信息 rosnode i…

Jmeter参数化 —— 循环断言多方法

1、参数化接口测试数据 注意:csv文档参数化,里面有多少条数据,就要在线程组里循环多少次,不然就只执行一次 2、添加配置元件-计数器 关于计数器: ①Starting Value:给定计数器的初始值; ②递增&#xff1a…

Java开发框架和中间件面试题(1)

1.什么是Spring框架? Spring是一种轻量级框架,旨在提高开发人员的开发效率以及系统的可维护性。 我们一般说的Spring框架就是Spring Framework,它是很多模块的集合,使用这些模块可以很方便的协助我们进行开发。这些模块是核心容器、数据访…

安卓手机如何打开ics文件?ics格式文件用什么软件打开?

什么是ics格式文件?Ics格式文件是什么呢?其实ics格式文件是一种用于保存和交换日历信息的标准格式,它通常可以保存事件的名称、时间等信息,有不少日历、待办软件在导出数据的时候,都是以ics文件导出的。 有不少网友目…

求职方略-倒金字塔型自我介绍

第一步,开头第一句话提纲挈领,点出你的主要“卖点” 自我介绍的第一句话很重要,要有足够的吸引力,有足够的信息量,还要有足够的说服力,能产生先声夺人的效果。 一般的自我介绍喜欢按照时间线索依次介绍自己的经历,例如:“我大学毕业后就进入一家大公司的研发中心,工…

java.lang.IllegalStateException: Duplicate key

序言 最近监控扫描出我们项目的某些异常信息,报错java.lang.IllegalStateException: Duplicate key xxx,看到异常来自stream流,然后定位看了一下是某位同事的代码使用stream流把List转Map集合出现重复的key异常信息。List集合A对象来源于某个…

C# WPF上位机开发(QT vs WPF)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 最近经常收到朋友们的私信,他们对C# WPF开发很感兴趣,但是呢,正当准备学习的时候,又有人告诉他们应…

Linux编程环境和软件设施安装

目录 一、Linux编程环境安装 1.yum安装 2. vim安装(文本编辑工具) 3. lrzsz安装(文件上传下载工具) (1) 搜索lrzsz安装包 (2) 在线安装lrzsz (3) 测试 二、Linux软件安装 1. 软件安装方…

Ubuntu 常用命令之 history 命令用法介绍

📑Linux/Ubuntu 常用命令归类整理 history命令在Ubuntu系统中用于显示用户执行过的命令列表。这个命令在bash shell中非常有用,特别是当你需要记住你之前执行过的命令时。 history命令的参数如下 -c:清除历史记录。-d offset:删…

Python如何生成随机图形验证码

python生成随机图形验证码 使用python生成随机图片验证码,需要使用pillow模块 1.安装pillow模块 pip install pillow 2.pillow模块的基本使用 1.创建图片 from PIL import Image #定义使用Image类实例化一个长为400px,宽为400px,基于RGB的(255,255,255)颜色的图片 img1Ima…

win11下配置visual studio 2022+PCL1.13.1

第一部分:visual studio2022 安装 vs官网网址如下:https://visualstudio.microsoft.com/zh-hans/vs/ 第一步:我们打开官网链接,按如下操作点击下载免费版本的exe文件 第二步:打开下载目录下的安装文件进行安装&#…

SQL 多字段条件查询

SQL 多字段条件查询 一个数据库表,几十个字段,查找任意字段里包含北京的记录,在 mysql 里这句 sql 应该是这样: SELECT * FROM table WHERE concat(field1,field2,field3……fieldn) like ‘% 北京 %’ 反正是少不了将几十个字…

【论文笔记】3D Gaussian Splatting for Real-Time Radiance Field Rendering

原文链接:https://arxiv.org/abs/2308.04079 1. 引言 网孔和点是最常见的3D场景表达,因其是显式的且适合基于GPU/CUDA的快速栅格化。神经辐射场(NeRF)则建立连续的场景表达便于优化,但渲染时的随机采样耗时且引入噪声…

Win11安装ise14.7~不需要虚拟机了~

之前一直无法在win11上安装ise14.7,网上搜索也无果,所有一直vmware虚拟机使用。直到最近看了水木上jesce的回复,试了下果然可以直接安装使用的。 步骤如下即可: 安装时切勿勾选最后一项,Enable WebTalk to send softwa…

Python (十五)pandas(三)

程序员的公众号:源1024,获取更多资料,无加密无套路! 最近整理了一波电子书籍资料,包含《Effective Java中文版 第2版》《深入JAVA虚拟机》,《重构改善既有代码设计》,《MySQL高性能-第3版》&…

【vtkWidgetRepresentation】第十六期 vtkContourRepresentation(三)

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 前言 本文分享vtkContourLineInterpolator接口的源码剖析和实例应用,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的点赞就是我的动力(^U^)ノ~YO 目录 前言 …

Collecting package metadata (current_repodata.json): failed(解决方案)

如果有重装过anaconda,在C盘的用户目录下,会有一个名叫.condarc的文件会自动生成。 当使用conda install和conda create命令会出现下面的问题:Collecting package metadata (current_repodata.json): failed 解决方案: 1.打开Anac…

深圳鼎信|配电网故障定位与预警装置_高效_快速

电能是我们生活和生产的一种主要能源。在电能供应过程中,配电网是电网的末端环节,但在能源转型的背景下,配电网逐渐成为电网发展的前沿阵地,将来也会越来越重要。 我国疆土辽阔,不同地区配电网的结构各具特色。总的来说…

openGauss学习笔记-169 openGauss 数据库运维-备份与恢复-导入数据-更新表中数据-使用DML命令更新表

文章目录 openGauss学习笔记-169 openGauss 数据库运维-备份与恢复-导入数据-更新表中数据-使用DML命令更新表169.1 操作步骤 openGauss学习笔记-169 openGauss 数据库运维-备份与恢复-导入数据-更新表中数据-使用DML命令更新表 openGauss支持标准的数据库操作语言&#xff08…