【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.rhkb.cn/news/222690.html

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

相关文章

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

📑Linux/Ubuntu 常用命令归类整理 chown 命令在 Ubuntu 系统中用于改变文件或目录的所有者和组。这个命令的基本格式是 chown [选项]... [所有者][:[组]] 文件...。 chown 命令的主要参数有 -c 或 --changes:类似 verbose,但只在发生改变时…

【泛型中K T V E? Object等分别代表什么含 义】

✅ 泛型中K T V E? Object等分别代表什么含义 ✅ 典型解析✅代码示例 ✅ 典型解析 E - Element (在集合中使用,因为集合中存放的是元素) T-Type (Java 类) K- Key (键) V - Value (值) N - Number (数值类型) ? - 表示不确定的iava类型 (无限制通配符类型) …

树莓派-Pico控制舵机

目录 前言一、SG90舵机是什么?参数介绍工作原理 二、与舵机信号线的接线图三、给树莓派Pico注入灵魂(代码)总结 前言 这价格便宜的树莓派Pico总觉得应该拿来做点什么,它总不能只用来点亮几个灯就没别的用途了吧,所以就…

自制数据库空洞率清理工具-C版-01-EasyClean-V1.0(支持南大通用数据库Gbase8a)

目录 一、环境信息 二、简述 三、支持功能 四、空洞率 五、工具流程图 六、安装包下载地址 七、参数介绍 1、命令模板 2、命令样例 3、参数表格 八、安装步骤 1、配置环境变量 2、生效环境变量 3、检验动态链接是否正常 九、运行效果 一、环境信息 名称值CPUInt…

力扣题目学习笔记(OC + Swift)19. 删除链表的倒数第 N 个结点

19. 删除链表的倒数第 N 个结点 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。 此题目为链表题,拿出我们的杀手锏,链表解题经典三把斧: 哑巴节点栈快慢指针 关于内存问题:由于Swift及…

R语言【cli】——通过cli_abort用 cli 格式的内容显示错误、警告或信息,内部调用cli_bullets和inline-makeup

cli_abort(message,...,call .envir,.envir parent.frame(),.frame .envir ) 先从那些不需要下大力气理解的参数入手: 参数【.envir】:进行万能表达式编译的环境。 参数【.frame】:抛出上下文。默认用于参数【.trace_bottom】&#xff…

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

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

【Amazon 实验①】Amazon WAF功能增强之实验环境准备

文章目录 1. 实验介绍2. 实验环境准备 1. 实验介绍 在真实的网络空间中,攻击者会使用大量广泛分布的僵尸网络、肉机等发起对目标的攻击。 其来源分布一般比较分散,因此难以简单防范。 本实验联合使用有多种AWS服务:Cloudfront、 Lambdaedge…

『番外篇五』SwiftUI 进阶之如何动态获取任意视图的 tag 和 id 值

概览 在某些场景下,我们需要用代码动态去探查 SwiftUI 视图的信息。比如任意视图的 id 或 tag 值: 如上图所示:我们通过动态探查技术在运行时将 SwiftUI 特定视图的 tag 和 id 值显示在了屏幕上。 这是如何做到的呢? 在本篇博文,您将学到如下内容: 概览1. “如意如意,…

Linux基本内容学习

Linux 命令 文件命令 命令释义语法格式lslist,用于显示目录中文件及其属性信息ls [参数名] [文件名]cdchange directory,用于更改当前所处的工作目录,路径可以是绝对路径,也可以是相对路径,若省略不写则会跳转至当前…

测试工具Jmeter:界面介绍、核心选项说明、核心选项用途

本文章主要介绍Jmeter的界面布局,以及各个选项的功能和它们的用途。 JMeter基本原理是建立一个线程池,多线程运行取样器产生大量负载,在运行过程中通过断言来验证结果的正确性,通过监听器来记录测试结果。 1. Jmeter主界面 当我…

Unresolved plugin: ‘org.apache.maven.plugins‘解决报错

新建springboot项目报Unresolved plugin: ‘org.apache.maven.plugins:maven-surefire-plugin:3.1.2’ 缺什么插件 引入什么插件的依赖就行 <dependency><groupId>org.apache.maven.plugins</groupId><artifactId>maven-install-plugin</artifact…

docker数据卷数据卷容器

前言 今天调休在家&#xff0c;随便玩玩&#xff0c;简单做下学习记录 1. 数据卷特点 数据卷在容器启动时初始化&#xff0c;如果容器使用的镜像在挂载点包含了数据&#xff0c;这些数据会被拷贝到新初始化的数据卷中数据卷可以在容器之间共享和重用可以对数据卷里的内容直接…

重学设计模式-Iterator(迭代器模式)

Iterator迭代器模式 介绍&#xff1a; 迭代器模式是一种行为型设计模式&#xff0c;它允许你在不暴露集合底层表示&#xff08;并不知道集合底层使用何种方式对数据尽心存储&#xff09;的情况下遍历集合中的元素。 这种模式提供了一种方法&#xff0c;可以顺序访问一个聚合…

Tomcat转SpringBoot、tomcat升级到springboot、springmvc改造springboot

Tomcat转SpringBoot、tomcat升级到springboot、springmvc改造springboot 起因&#xff1a;我接手tomcat-springmvc-hibernate项目&#xff0c;使用tomcat时问题不大。自从信创开始&#xff0c;部分市场使用国产中间件&#xff0c;例如第一次听说的宝兰德、东方通&#xff0c;还…

让AIGC成为你的智能外脑,助力你的工作和生活

人工智能成为智能外脑 在当前的科技浪潮中&#xff0c;人工智能技术正在以前所未有的速度改变着我们的生活和工作方式。其中&#xff0c;AIGC技术以其强大的潜力和广泛的应用前景&#xff0c;正在引领着这场革命。 AIGC技术是一种基于人工智能的生成式技术&#xff0c;它可以通…

光模块市场分析与发展趋势预测

光模块是光通信领域的重要组成部分&#xff0c;随着数字经济&#xff0c;大数据&#xff0c;云计算&#xff0c;人工智能等行业的兴起&#xff0c;光模块市场经历了快速发展&#xff0c;逐渐在数据中心、无线回传、电信传输等应用场景中得到广泛应用。本文将基于当前光模块全球…

[JS设计模式]Flyweight Pattern

Flyweight pattern 享元模式是一种结构化的设计模式&#xff0c;主要用于产生大量类似对象而内存又有限的场景。享元模式能节省内存。 假设一个国际化特大城市SZ&#xff1b;它有5个区&#xff0c;分别为nanshan、futian、luohu、baoan、longgang&#xff1b;每个区都有多个图…

STM32F4的DHT11初始化与实例分析

STM32—— DHT11 本文主要涉及STM32F4 的DHT11的使用以及相关时序的介绍&#xff0c;最后有工程下载地址。 文章目录 STM32—— DHT11一、 DHT11的介绍1.1 DHT11的经典电路 二、DHT11的通信2.1 DHT11的传输数据格式2.2 DHT11 通信分步解析 三、 DHT11 代码3.1 引脚图3.2 电路图…

设计师都在用的5个设计灵感网站,赶紧收藏~

设计没灵感&#xff0c;就上这5个网站&#xff0c;绝对能打开新世界大门&#xff0c;全世界设计师的分享、互交站&#xff0c;最优秀的设计作品都在这&#xff0c;赶紧收藏好了~ pinterest​&#xff08;梯子&#xff09; https://www.pinterest.es/ Pinterest是以瀑布流的方式…