JIT详解

文章目录

  • JIT
        • 为什么说 Java 语言“编译与解释并存”?
    • JIT原理
      • JVM 架构简览
      • JIT 编译流程
      • JIT 编译器的实现
        • 优化策略
            • 方法内联
            • 逃逸分析

JIT

在Java中,JIT(Just-In-Time)编译器是Java虚拟机(JVM)的一个重要组成部分,它负责将Java字节码转换成特定平台的机器码。这个过程是在Java程序运行时进行的,而非传统的编译过程中。Java的JIT编译器的目的是提高程序的执行效率,特别是对于长时间运行的Java应用来说,JIT编译可以显著提高性能。

这里是Java中代码从编译到运行的基本流程:

  1. 编译到字节码:首先,Java源代码被编译成Java字节码(.class文件)。这一步是静态的,发生在程序运行之前。
  2. 类加载:当Java程序运行时,Java虚拟机(JVM)会加载这些字节码文件。
  3. 字节码解释执行:最初,JVM通过解释器逐条执行字节码。这意味着JVM读取每条指令,然后执行对应的操作。这种方式简单但效率不高。
  4. JIT编译:当JVM识别出某些方法或代码块被频繁调用(即“热点代码”),它会使用JIT编译器将这些热点代码从字节码编译成本地机器码。由于机器码可以直接在硬件上执行,这样可以显著提高程序的执行速度。
  5. 优化:JIT编译器在编译过程中还会进行各种优化,比如内联、循环展开等,这些优化可以进一步提升代码的执行效率。

Java 程序从源代码到运行的过程如下图所示

Java程序转变为机器代码的过程Java程序转变为机器代码的过程

我们需要格外注意的是 .class->机器码 这一步。在这一步 JVM 类加载器首先加载字节码文件,然后通过解释器逐行解释执行,这种方式的执行速度会相对比较慢。而且,有些方法和代码块是经常需要被调用的(也就是所谓的热点代码),所以后面引进了 JIT(Just in Time Compilation) 编译器,而 JIT 属于运行时编译。当 JIT 编译器完成第一次编译后,其会将字节码对应的机器码保存下来,下次可以直接使用。而我们知道,机器码的运行效率肯定是高于 Java 解释器的。这也解释了我们为什么经常会说 Java 是编译与解释共存的语言

Java程序转变为机器代码的过程

JDK、JRE、JVM、JIT 这四者的关系如下图所示。

JDK、JRE、JVM、JIT 这四者的关系

为什么说 Java 语言“编译与解释并存”?

我们可以将高级编程语言按照程序的执行方式分为两种:

  • 编译型:编译型语言 会通过编译器将源代码一次性翻译成可被该平台执行的机器码。一般情况下,编译语言的执行速度比较快,开发效率比较低。常见的编译性语言有 C、C++、Go、Rust 等等。
  • 解释型:解释型语言会通过解释器一句一句的将代码解释(interpret)为机器代码后再执行。解释型语言开发效率比较快,执行速度比较慢。常见的解释性语言有 Python、JavaScript、PHP 等等。

编译型语言和解释型语言编译型语言和解释型语言

根据维基百科介绍:

为什么说 Java 语言“编译与解释并存”?

这是因为 Java 语言既具有编译型语言的特征,也具有解释型语言的特征。因为 Java 程序要经过先编译,后解释两个步骤,由 Java 编写的程序需要先经过编译步骤,生成字节码(.class 文件),这种字节码必须由 Java 解释器来解释执行。

JIT原理

要深入理解Java中JIT(Just-In-Time)编译器的原理,我们需要从Java虚拟机(JVM)的架构和JIT编译的工作流程入手。

JVM 架构简览

JVM主要由以下几个部分组成:

  1. 类加载器(Class Loaders):负责加载类文件。
  2. 运行时数据区(Runtime Data Areas):存储各种运行时数据,包括堆(Heap)、栈(Stacks)、方法区(Method Area)、程序计数器(Program Counter Register)等。
  3. 执行引擎(Execution Engine):将字节码转换为机器码并执行。执行引擎中包括解释器(Interpreter)和JIT编译器。

JIT 编译流程

JIT编译器的工作流程大致如下:

  1. 解释执行:最初,执行引擎使用解释器逐条解释执行字节码。这种执行方式容易实现,但效率不高。
  2. 识别热点代码:JVM监控各个方法的执行情况,识别出被频繁执行的方法或代码块,即“热点代码”(Hot Spot Code)。
  3. 编译热点代码:JIT编译器将这些热点代码编译成相应平台的机器码。这个过程中,JIT编译器还会应用多种优化技术,比如方法内联、循环展开等,以提高代码的执行效率。
  4. 替换与执行编译生成的机器码将替换原先的字节码执行路径。当再次执行到这些代码时,JVM将直接执行对应的机器码,从而提高执行效率。

JIT 编译器的实现

Java中的JIT编译器实现较为复杂,主要体现在优化策略和代码生成上。比如,OpenJDK中就包含了一个非常著名的JIT编译器:HotSpot VM。

优化策略

JIT编译器在将字节码转换成机器码的过程中,会应用一系列的优化策略来提高程序的执行效率。这些优化策略主要包括:

  • 死码删除(Dead Code Elimination):编译器会识别并删除那些不会影响程序最终结果的代码段,例如永远不会被执行到的代码(dead code)。
  • 循环优化(Loop Optimization):循环是程序中常见的热点,因此循环优化是JIT编译器的重点。这包括循环展开(减少循环次数来减小循环开销)和循环融合等技术。
  • 方法内联(Method Inlining):将一个方法的内容直接替换到调用该方法的位置,以减少方法调用的开销。如果一个方法较小且频繁被调用,将其内联可以显著提高性能。
  • 逃逸分析(Escape Analysis):分析对象的作用域和生命周期,如果对象不会“逃逸”出方法或线程,可能会被优化成栈上分配,减少垃圾收集的压力。
方法内联

方法内联它会把一些短小的方法体,直接纳入目标方法的作用范围之内,就像是直接在代码块中追加代码。这样,就少了一次方法调用,执行速度就能够得到提升,这就是方法内联的概念。

可以使用 -XX:-Inline 参数来禁用方法内联,如果想要更细粒度的控制,可以使用 CompileCommand 参数,例如:

ini代码解读
复制代码-XX:CompileCommand=exclude,java/lang/String.indexOf

在 JDK 的源码里,也有很多被 @ForceInline注解的方法,这些方法,会在执行的时候被强制进行内联;而被@DontInline注解的方法,则始终不会被内联。

JIT 编译之后的二进制代码,是放在 Code Cache 区域里的。这个区域的大小是固定的,而且一旦启动无法扩容。如果 Code Cache 满了,JVM 并不会报错,但会停止编译。所以编译执行就会退化为解释执行,性能就会降低。不仅如此,JIT 编译器会一直尝试去优化你的代码,造成 CPU 占用上升。

通过参数 -XX:ReservedCodeCacheSize 可以指定 Code Cache 区域的大小,如果你通过监控发现空间达到了上限,就要适当的增加它的大小。

逃逸分析

下面着重讲解一下逃逸分析,这个知识点在面试的时候经常会被问到。

有这样一个问题:我们常说的对象,除了基本数据类型,一定是在堆上分配的吗?

答案是否定的,通过逃逸分析,JVM 能够分析出一个新的对象的使用范围,从而决定是否要将这个对象分配到堆上。逃逸分析现在是 JVM 的默认行为,可以通过参数-XX:-DoEscapeAnalysis 关掉它。

那什么样的对象算是逃逸的呢?可以看一下下面的两种典型情况。

如代码所示,对象被赋值给成员变量或者静态变量,可能被外部使用,变量就发生了逃逸。

public class EscapeAttr {Object attr;public void test() {attr = new Object();}
}

再看下面这段代码,对象通过 return 语句返回。由于程序并不能确定这个对象后续会不会被使用,外部的线程能够访问到这个结果,对象也发生了逃逸。

public class EscapeReturn {Object attr;public Object test() {Object obj = new Object();return obj;}
}

那逃逸分析有什么好处呢?

「1. 栈上分配」

如果一个对象在子程序中被分配,指向该对象的指针永远不会逃逸,对象有可能会被优化为栈分配。栈分配可以快速地在栈帧上创建和销毁对象,不用再分配到堆空间,可以有效地减少 GC 的压力。

「2. 分离对象或标量替换」

但对象结构通常都比较复杂,如何将对象保存在栈上呢?

JIT 可以将对象打散,全部替换为一个个小的局部变量,这个打散的过程,就叫作标量替换(标量就是不能被进一步分割的变量,比如 int、long 等基本类型)。也就是说,标量替换后的对象,全部变成了局部变量,可以方便地进行栈上分配,而无须改动其他的代码。

从上面的描述我们可以看到,并不是所有的对象或者数组,都会在堆上分配。由于JIT的存在,如果发现某些对象没有逃逸出方法,那么就有可能被优化成栈分配。

「3.同步消除」

如果一个对象被发现只能从一个线程被访问到,那么对于这个对象的操作可以不考虑同步。

注意这是针对 synchronized 来说的,JUC 中的 Lock 并不能被消除。

要开启同步消除,需要加上 -XX:+EliminateLocks 参数。由于这个参数依赖逃逸分析,所以同时要打开 -XX:+DoEscapeAnalysis 选项。

比如下面这段代码,JIT 判断对象锁只能被一个线程访问,就可以去掉这个同步的影响。

public class SyncEliminate {public void test() {synchronized (new Object()) {}}
}

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

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

相关文章

韩国 NEL动物医疗中心:犬二尖瓣修复术连获成功

“守护心脏的神之手” 在小型犬心脏疾病根本治疗(犬心脏修复手术)方面取得重大突破,已成功完成 16 例手术。 韩国 NEL动物医疗中心(NEL ANIMAL MEDICAL CENTER)严泰欽院长带领的心脏手术团队,为 16 只宠物…

Lumerical学习——分析工具(Analysis tools)

一、分析工具和模拟环境(Analysis tools and the simulation environment) 模拟计算完成后,模拟计算数据紧接着写到模拟工程文件中;甚至当模拟计算提前结束时计算得到的部分数据集也会写到文件中。当模拟完成后单击退出按钮、或者…

【行业观察】三折机不够看?苹果研发四折叠屏iPhone

随着智能手机市场的日益成熟,消费者对手机形态和功能的需求也在不断升级。近年来,折叠屏手机凭借其独特的形态和更大的显示面积,成为了市场的新宠。而在这场折叠屏手机的竞争中,苹果作为智能手机行业的领头羊,自然也不…

Jmeter脚本录制、Badboy脚本录制

目录 Jmeter脚本录制 Badboy脚本录制 Jmeter脚本录制 1、首先添加一个http测试脚本记录器 2、选择目标控制器,把录制的内容放到的地方 3、分组,就是在录制的时候,每一步操作之间间隔方式,根据自己需要进行选择 4、开启浏览…

AI开发-三方库-Hugging Face-Pipelines

1 需求 需求1:pipeline支持的任务类型 需求2:推理加速使用CPU还是GPU 需求3:基于pipeline的文本分类示例 需求4:pipeline实现原理 模型使用步骤(Raw text -》Input IDs -》Logits -》Predictions)&…

跨域问题及常用的5种解决方案

1.什么是跨域问题? 跨域问题通常指的是在浏览器中由于同源策略的限制而产生的问题。同源策略(Same-origin policy)是浏览器的一种安全措施,它要求请求的域名、协议和端口必须与提供资源的网站相同。当一个网页尝试访问另一个来源…

基于SpringBoot+Vue+uniapp的在线招聘平台的详细设计和实现

详细视频演示 请联系我获取更详细的演示视频 项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念,提供了一套默认的配置,让开发者可以更专注于业务逻辑而不…

图论day62|拓扑排序理论基础、117.软件构建(卡码网)、最短路径之dijkstra理论基、47.参加科学大会(卡码网 第六期模拟笔试)

图论day62|拓扑排序理论基础、117.软件构建(卡码网)、最短路径之dijkstra理论基、47.参加科学大会(卡码网 第六期模拟笔试) 拓扑排序理论基础117.软件构建(卡码网)最短路径之dijkstra理论基础47.参加科学大…

IT招聘乱象的全面分析

近年来,IT行业的招聘要求似乎越来越苛刻,甚至有些不切实际。许多企业在招聘时,不仅要求前端工程师具备UI设计能力,还希望后端工程师精通K8S服务器运维,更有甚至希望研发经理掌握所有前后端框架和最新开发技术。这种招聘…

day-67 二叉树展开为链表

思路 深度优先遍历:首先将二叉树进行前序遍历,将每个遍历的节点存入一个链表当中 解题过程 将链表还原为只有右子树的二叉树即可 Code /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* Tr…

Hi3061M开发板——系统时钟频率

这里写目录标题 前言MCU时钟介绍PLLCRG_ConfigPLL时钟配置另附完整系统时钟结构图 前言 Hi3061M使用过程中,AD和APT输出,都需要考虑到时钟频率,特别是APT,关系到PWM的输出频率。于是就研究了下相关的时钟。 MCU时钟介绍 MCU共有…

认识Java的异常

异常机制 异常机制指的是程序出现错误时,程序的处理方式。 程序的错误分为三种: 编译错误:由于没有遵循对于语言的语法规则,编辑器可以自动发现并提示的错误位置和原因。逻辑错误:程序没有按照预期的顺序执行。运行…

FreeRTOS应用开发学习

了解FreeRTOS 任务相关API FreeRTOS任务创建API FreeRTOS 中,任务的创建有两种方法,一种是使用动态创建,一种是使用静态创建。动态创建时,任务控制块和栈的内存是创建任务时动态分配的,任务删除时,内存可…

申请商家转账到零钱功能所需材料及必过攻略

商家转账到零钱功能的快速开通方法,可以归纳为以下几个步骤: 一、确认商户资格与账号状态 1. 商户类型:该功能通常只对公司性质的商户开放,个体工商户及小微商户暂时无法申请。商家需为微信支付认证用户。 2. 账号状态&#xff…

网络层及ip报头

★★★★★默写: A类:0~127 B类:128~191 C类:192~223 A类私网:10.0.0 - 10.255.255.255 B类私网:172.16.0.0 - 172.31.255.255 C类私网:19.168.0.0 - 192.168.255.255 特殊: 0.0.0…

新手爬虫DAY1

这个错误信息表明在你的Python程序中,re.search() 函数没有找到预期的匹配项,因此返回了 None。当你尝试在 None 对象上调用 group(1) 方法时,Python 抛出了一个 AttributeError。 具体来说,错误发生在 pc.py 文件的第6行&#x…

AI大模型与相对论的结合点的思考、应用及相对论原理与公式表达

大家好,我是微学AI,今天给大家介绍一下AI大模型与相对论的结合点的思考、应用及相对论原理与公式表达。在阐述相对论原理的基础上,通过数学复杂公式,分析了人工智能大模型在相对论领域的应用前景。文章深入挖掘了两大领域之间的联…

厨房老鼠数据集:掀起餐饮卫生监测的科技浪潮

厨房老鼠数据集:掀起餐饮卫生监测的科技浪潮 摘要:本文深入探讨了厨房老鼠数据集在餐饮行业卫生管理中的重要性及其相关技术应用。厨房老鼠数据集通过收集夜间厨房图像、老鼠标注信息以及环境数据,为深度学习模型提供了丰富的训练样本。基于…

MongoDB 安装配置及配置和启动服务

MongoDB 安装配置 附:MongoDB官网下载地址: https://www.mongodb.com/download-center/community 注: 官网可以下载最新版的MongoDB安装包,有MSI安装版和ZIP安装版。我们课堂上使用4.4.4的ZIP安装版。安装版参考博客&#xff1…

Spark第一天

MapReduce过程复习 Spark由五部分组成 RDD五大特征 1、 Spark -- 代替MapReduce <<<<< scala是单机的&#xff0c;spark是分布式的。>>>>> 开源的分布式计算引擎 可以快速做计算 -- 因为可以利用内存来做一些计算 (1) 分为5个库(模块) : 1、…