当前位置: 首页 > news >正文

即时编译器JIT

类编译加载执行过程

如下图所示,一个Java代码从编译到运行大抵会经历以下几个过程。具体每个过程笔者会在下文站展开讨论。

类编译

首先是类编译阶段,这个阶段会将Java文件变为class文件,这个class文件包含一个常量池和方法表集合,而方法表集合里面会包含方法访问权限、返回类型、JVM执行指令以及属性集合等信息。

类加载

对于没有加载的类,JVM就会拿着这个class文件进行类加载,JDK自带的本地方法在双亲委派机制下,会用根加载器(Bootstrp loader)进行加载,而JDK扩展方法则会由扩展加载器(ExtClassLoader ),我们应用程序自己写的方法则是由系统加载器(AppClassLoader )完成加载。

完成加载后,常量池或者每个类的字段描述符、方法描述符等信息都会加载到JVM的方法区,同时会在堆区生成一个代表这个类的java.lang.Class对象,作为这个类的各种数据的访问入口。

类连接

类连接就是验证、准备、初始化3个过程了。

  1. 验证:验证类符合 Java 规范和 JVM 规范,在保证符合规范的前提下,避免危害虚拟机安全。
  2. 准备: 为类的静态变量分配内存,初始化为系统初始值。private final static int value=123,在这一步就完成空间分配和初始赋值为0。而private final int num=123则会在这一步直接赋值为123。因为它是一个常量。
  3. 解析:将编译器每个类的符号引用(包括类和接口的全限定名、类引用、方法引用以及成员变量引用等)等信息转为直接引用(JVM可直接获取的内存地址或指针)
类初始化

JVM会执行构造器的<cinit>,收集所有类、方法、静态变量的初始化静态变量赋值、静态代码块、静态方法,然后按顺序从上到下执行。

注意笔者说的,按顺序从上到下,这就意味的静态变量的完成初始化后的结果是以最后一个初始化语句为准。如下所示

赋值语句在后,结果为1


public class Main {static  {num = 2;}private static int num = 1;public static void main(String[] args) {System.out.println(num);//1}
}

 静态代码块在后,结果为2

public class Main {private static int num = 1;static  {num = 2;}public static void main(String[] args) {System.out.println(num);//1}
}

即时编译(重点)

在初始化阶段完成后,执行引擎不断将调用到的字节码翻译成机器码交由计算机执行。Java字节码转为机器码之间还有一步转换,我们称之为既时编译

最初Java字节码文件是直接通过解释器( Interpreter )解释为机器码直接运行的。
后来设计者们考虑到某些执行频率比较高的代码,我们可以称之为热点代码,可以进行某些机制进行优化(例如对代码逻辑进行优化缓存到本地内存中)
所以,我们如今编写的Java代码若执行频率非常高的话,就会被判定为热点代码,那么即时编译器,就会对这类代码进行逻辑优化,编译为最优的本地机器码保存到内存中。


著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

即时编译器类型有哪些?

HotSpot 虚拟机中内置了两个JIT编译器,分别为:

  1. C1编译器:主要关注点在于局部性优化,常用于那些执行时间短,或者要求快速启动的应用程序,例如GUI应用程序。
  2. C2编译器:常用于长期运行且对峰值性能有高要求的服务器。

所以我们也称C1编译器和C2编译器为 Client Compiler或者Server Compiler

在 Java7 之前,需要根据程序的特性来选择对应的 JIT,虚拟机默认采用解释器和其中一个编译器配合工作。
Java7 引入了分层编译,这种方式综合了 C1 的启动性能优势和 C2 的峰值性能优势,我们也可以通过参数 “-client”“-server” 强制指定虚拟机的即时编译模式。分层编译将 JVM 的执行状态分为了 5 个层次:
第 0 层:程序解释执行,默认开启性能监控功能(Profiling),如果不开启,可触发第二层编译;
第 1 层:可称为 C1 编译,将字节码编译为本地代码,进行简单、可靠的优化,不开启 Profiling;
第 2 层:也称为 C1 编译,开启 Profiling,仅执行带方法调用次数和循环回边执行次数 profiling 的 C1 编译;
第 3 层:也称为 C1 编译,执行所有带 Profiling 的 C1 编译;
第 4 层:可称为 C2 编译,也是将字节码编译为本地代码,但是会启用一些编译耗时较长的优化,甚至会根据性能监控信息进行一些不可靠的激进优化。

在 Java8 中,默认开启分层编译,-client 和 -server 的设置已经是无效的了。如果只想开启
C2,可以关闭分层编译(-XX:-TieredCompilation),如果只想用
C1,可以在打开分层编译的同时,使用参数:-XX:TieredStopAtLevel=1。

我们可以使用java -version查看当前编译的编译模式,可以看到笔者服务器的JVM使用的就是混合编译模式


[root@iZ8vb7bhe4b8nhhhpavhwpZ ~]# java -version
java version "1.8.0_202"
Java(TM) SE Runtime Environment (build 1.8.0_202-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.202-b08, mixed mode)
如果我们想强制运行JIT编译模式,也可以使用
java -Xint -version

如果我们想强制运行JIT编译模式,也可以使用

java -Xcomp -version

热点探测了解过吗(重点)

HotSpot 虚拟机判定热点代码是基于两种计数器进行的,分别是方法调用计数器(Invocation Counter)回边计数器(Back Edge Counter),只有执行代码符合他们的标准且达到他的设置的阈值时才会进行JIT编译优化。

方法调用计数器

这个计数器工作机制非常好理解,当某个方法执行次数超过阈值时,就会触发JIT编译优化,这个阈值我们可以通过jinfo查看,如下所示,可以看到笔者JVM设置的方法调用计数器判定是否是热点代码的条件为调用次数达到10000次。

[root@xxx~]# jinfo -flag CompileThreshold 2341
-XX:CompileThreshold=10000
回边计数器

在字节码遇到控制流后跳转的操作我们称之为回边。回边计数器判定代码为热点代码的条件是:一个代码在循环体内达到回边计数器要求的阈值,而这个阈值我们也可以通过jinfo查看

[root@xxx~]# jinfo -flag OnStackReplacePercentage 2341
-XX:OnStackReplacePercentage=140

当这段代码被判定为热点代码时,JVM就会进行一种栈上编译的优化操作,它会将这段代码编译为最优逻辑保存到本地内存,在执行循环体的期间,直接使用缓存中的机器码。

JIT自动进行的编译优化技术(重要)

方法内联

我们都知道方法调用会经历一个压栈和出栈的操作,执行调用方法时会将地址转移到存储该方法的起始地址上,待调用结束后,在返回原来的位置。
这就意味着一个方法调用另一个方法时,就需要保存当前方法执行位置,栈上压入被调用方法,执行完成后,恢复现场继续执行之前执行的方法。因此方法调用期间是有一定的时间和空间的开销的。

所以JIT会对那些方法调用方法非常频繁的代码执行方法内敛,如下所示:

private int add1(int x1, int x2, int x3, int x4) {return add2(x1, x2) + add2(x3, x4);
}
private int add2(int x1, int x2) {return x1 + x2;
}

最终会被优化为:

private int add1(int x1, int x2, int x3, int x4) {return x1 + x2 + x3 + x4;
}

但是方法内敛优化也是有条件的,除了必须是热点代码(达到XX:CompileThreshold的阈值)以外,还要达到以下要求:

  1. 对于经常执行的方法,方法体要小于325字节,这个字节数可以通过-XX:MaxFreqInlineSize=N来调整。
  2. 对于不经常执行的方法,方法体要小于35字节,这个字节数可以由-XX:MaxInlineSize=N 来调整。

我们不妨看一段代码,可以看到add1执行了1000000

public class JVMJit {public static void main(String[] args) {for (int i = 0; i < 1000000; i++) {add1(1, 2, 3, 4);}}private static int add1(int i, int i1, int i2, int i3) {return i + i1 + i2 + i3;}}

我们可以对这段程序添加这样一段参数查详情-XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining

他们的含义分别是

-XX:+PrintCompilation // 在控制台打印编译过程信息
-XX:+UnlockDiagnosticVMOptions // 解锁对 JVM 进行诊断的选项参数。默认是关闭的,开启后支持一些特定参数对 JVM 进行诊断
-XX:+PrintInlining // 将内联方法打印出来

可以看到这段代码被判定为热点代码,说明他已经被JVM优化了

所以这就要求我们平时写代码时:

  1. 方法体尽可能小
  2. 尽可能使用privatefinalstatic修饰,避免一些没必要的类是否继承等相关检查。
栈上分配

在将栈上分配前,我们需要先了解一个叫逃逸分析(Escape Analysis)的技术。
逃逸分析就是判断当前操作的对象是否有被外部方法引用或外部线程访问的一种技术,若逃逸分析判定当前对象并没有被其他引用或者线程使用到的话,某些机制就可以开始进行优化,比如我现在要说的栈上分配。

我们都知道创建一个对象,都是在堆上分配的,假如这个对象使用封闭,GC就会将其回收,而创建和回收这一来一回的操作也是有一定开销的。而栈则不一样,它使用的引用或者各种变量随着调用的结束就消亡。

而栈上分配就是抓住这一特点,当他经过逃逸分析技术发现这个对象并没有被外部引用且仅在当前线程使用,那么它就会将该对象分配在栈上。如下面这样一段代码:

public static void main(String[] args) {for (int i = 0; i < 200000 ; i++) {getAge();}
}public static int getAge(){Student person = new Student(" 小明 ",18,30);   return person.getAge();
}static class Student {private String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}...get set
}

但是,在 HotSpot 中暂时没有实现这项优化。随着即时编译器的发展与逃逸分析技术的逐渐成熟,相信不久的将来 HotSpot 也会实现这项优化功能。

锁消除

同样在逃逸分析某些没有被外部方法或者其他线程引用的情况下,会将某些锁消除。例如下面这段代码,实际上你在运行时可以发现StringBufferStringBuilder 性能上没有什么区别,这正是因为锁消除为我们做的优化工作。

public static String getString(String s1, String s2) {StringBuffer sb = new StringBuffer();sb.append(s1);sb.append(s2);return sb.toString();}
标量替换

当一个代码的对象在方法上可以拆分,并且代码仅仅是对这个对象的变量进行各种操作的话,编译器可能会执行标量替换,如下所示

  public void foo() {TestInfo info = new TestInfo();info.id = 1;info.count = 99;...//to do something}

由于上述代码仅仅是创建一个对象后操作对象的变量,实际上这个工作似乎和对象没有任何关联,编译器识别到这点之后就不去创建没必要的对象,进而使用标量替换的方式将对象的成员变量放到栈上,避免没必要的对象创建和销毁。

  public void foo() {id = 1;count = 99;...//to do something}

我们可以通过设置 JVM 参数来开关逃逸分析,还可以单独开关同步消除和标量替换,在 JDK1.8 中 JVM 是默认开启这些操作的。

-XX:+DoEscapeAnalysis 开启逃逸分析(jdk1.8 默认开启,其它版本未测试)
-XX:-DoEscapeAnalysis 关闭逃逸分析-XX:+EliminateLocks 开启锁消除(jdk1.8 默认开启,其它版本未测试)
-XX:-EliminateLocks 关闭锁消除-XX:+EliminateAllocations 开启标量替换(jdk1.8 默认开启,其它版本未测试)
-XX:-EliminateAllocations 关闭就可以了

相关文章:

即时编译器JIT

类编译加载执行过程 如下图所示&#xff0c;一个Java代码从编译到运行大抵会经历以下几个过程。具体每个过程笔者会在下文站展开讨论。 类编译 首先是类编译阶段&#xff0c;这个阶段会将Java文件变为class文件&#xff0c;这个class文件包含一个常量池和方法表集合&#xf…...

npm更新包时This operation requires a one-time password.

[访问我的npm包](mhfwork/yt-ui - npm) 更新npm包时出现 This operation requires a one-time password.是因为需要认证 解决办法 1. 点击红线处的链接 2. 进入npm官网获取指定秘钥 3. 再次填入 one-time password 即可...

C++类模板再学习

之前已经学习了C类模板&#xff1b;类模板的写法和一般类的写法有很大的差别&#xff1b;不容易熟悉&#xff1b;下面再做一遍&#xff1b; 做一个椭圆类&#xff0c;成员有长轴长度和短轴长度&#xff1b; // ellipse.h: interface for the ellipse class. // //#if !define…...

华为终端智能家居应用方案

PLC-IoT概述 华为智能PLC-IoT工业物联网系列通信模块是基于电力线宽带载波技术的产品&#xff0c;实现数据在电力线上双向、高速、稳定的传输&#xff0c;广泛适用于电力、交通、工业制造、智能家居等领域&#xff0c;PLC-IoT通信模块包含头端和尾端两种类型&#xff0c;头端配…...

PHP下载文件

/***文件下载*param $filepath源文件路径 */function dwon_file($filepath){if(file_exists($filepath)){header(content-type:text/html;charsetutf8);header(Content-Description: File Transfer);header(Content-Type: application/octet-stream);header(Content-Dispositio…...

38基于matlab的期货预测,利用PSO优化SVM和未优化的SVM进行对比,得到实际输出和期望输出结果。

基于matlab的期货预测&#xff0c;利用PSO优化SVM和未优化的SVM进行对比&#xff0c;得到实际输出和期望输出结果。线性核函数、多项式、RBF核函数三种核函数任意可选&#xff0c;并给出均方根误差&#xff0c;相对误差等结果&#xff0c;程序已调通&#xff0c;可直接运行。 3…...

【Codeforces】 CF582D Number of Binominal Coefficients

题目链接 CF方向 Luogu方向 题目解法 看到 p α ∣ ( n k ) p^{\alpha} | \binom{n}{k} pα∣(kn​) &#xff0c;首先想到 k u m m e r kummer kummer 定理&#xff0c;那么限制即为 n − k n-k n−k 和 k k k 做加法在 p p p 进制下的进位数 ≥ α \ge \alpha ≥α …...

sql第二次上机作业

1查找借阅了ISBN为“4-6045-1023-4”的借书证号&#xff0c;读者姓名&#xff0c;专业名和借书时间 use tsgl go select Reader.Lno,Rname,Spec,Lend.Bordate FROM Reader,Lend WHERE Reader.LnoLend.Lno AND ISBN 4-6045-1023-42查找借阅了《数据库原理》一书的借阅信息&…...

辅助驾驶功能开发-功能规范篇(22)-3-L2级辅助驾驶方案功能规范

1.3.3 TLA系统功能定义 1.3.3.1 状态机 1.3.3.2 状态迁移图 1.3.3.3 功能定义 1.3.3.3.1 信号需求列表 1.3.3.3.2 系统开启关闭 1)初始化 车辆上电后,交通灯辅助系统(TLA)进行初始化,控制器需在 220ms 内发出第一帧报文,并在 3s 内完成内部自检,同时上电 3s 内不进行…...

Python基础入门例程16-NP16 发送offer(列表)

目录 描述 输入描述&#xff1a; 输出描述&#xff1a; 解答 &#xff1a; 说明&#xff1a; 描述 某公司在面试结束后&#xff0c;创建了一个依次包含字符串 Allen 和 Tom 的列表offer_list&#xff0c;作为通过面试的名单。 请你依次对列表中的名字发送类似 Allen, you…...

Web前端面试之Vue—对Vue的理解

目录 一、web发展历程 二、vue是什么 三、Vue核心特性 组件化 数据驱动 指令 四、Vue与Angular以及React的区别 一、web发展历程 Web是World Wide Web的简称&#xff0c;中文译为万维网 我们可以将它规划成如下的几个时代来进行理解 静态网页&#xff1a;最早的网页是没…...

C/C++晶晶赴约会 2020年12月电子学会青少年软件编程(C/C++)等级考试一级真题答案解析

目录 C/C晶晶赴约会 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 C/C晶晶赴约会 2020年12月 C/C编程等级考试一级编程题 一、题目要求 1、编程实现 晶晶的朋友贝贝约晶晶下周一起去看展览&#xff0…...

js 解决 H 指数

给你一个整数数组 citations &#xff0c;其中 citations[i] 表示研究者的第 i 篇论文被引用的次数。计算并返回该研究者的 h 指数。 根据维基百科上 h 指数的定义&#xff1a;h 代表“高引用次数” &#xff0c;一名科研人员的 h 指数 是指他&#xff08;她&#xff09;至少发…...

在JS中,var 、let 、const 总结

let是英文单词"let"的缩写。在JavaScript中&#xff0c;let 关键字用来声明一个块级作用域 的变量&#xff0c;这意味着变量仅在声明它的代码块内有效&#xff0c;超出该代码块作用域时就无法访问该变量。与var不同的是&#xff0c;let不会被提升到函数作用域或全局作…...

关于网络安全运营工作与安全建设工作的一些思考

以下内容是个人成长过程中对于网络安全运营工作的理解和思考&#xff0c;希望通过这篇文章帮助大家更好的去做安全运营体系化建设&#xff0c;开始吧&#xff01; 文章目录 一、网络安全运营是什么&#xff1f;二、网络安全运营建设阶段第一阶段&#xff1a;设备限制阶段第二阶…...

【机器学习可解释性】4.SHAP 值

机器学习可解释性 1.模型洞察的价值2.特征重要性排列3.部分依赖图4.SHAP 值5.SHAP 值 高级使用 正文 理解各自特征的预测结果&#xff1f; 介绍 您已经看到(并使用)了从机器学习模型中提取一般解释技术。但是&#xff0c;如果你想要打破模型对单个预测的工作原理? SHAP 值…...

OpenCV官方教程中文版 —— 直方图均衡化

OpenCV官方教程中文版 —— 直方图均衡化 前言一、原理二、 OpenCV 中的直方图均衡化三、 CLAHE 有限对比适应性直方图均衡化 前言 本小节我们要学习直方图均衡化的概念&#xff0c;以及如何使用它来改善图片的对比。 一、原理 想象一下如果一副图像中的大多是像素点的像素值…...

如何使用navicat图形化工具远程连接MariaDB数据库【cpolar内网穿透】

公网远程连接MariaDB数据库【cpolar内网穿透】 文章目录 公网远程连接MariaDB数据库【cpolar内网穿透】1. 配置MariaDB数据库1.1 安装MariaDB数据库1.2 测试局域网内远程连接 2. 内网穿透2.1 创建隧道映射2.2 测试随机地址公网远程访问3. 配置固定TCP端口地址3.1 保留一个固定的…...

【uniapp】uview1.x使用upload上传图片

和2.x不同的是&#xff0c;要用 action 来配置后端上传图片的接口地址&#xff1b; 再来一些配置项的命名有所不同&#xff0c;一般1.x的命名用 -&#xff0c;2.x的命名使用小驼峰&#xff1b; 1.x 的上传会自带删除时的提示框&#xff0c;2.x 没有&#xff1b; 重要的几个配置…...

基于nodejs+vue食力派网上订餐系统

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时&#xff0c;可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案&#xff1a; 1. 检查电源供电问题 问题原因&#xff1a;多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

零基础设计模式——行为型模式 - 责任链模式

第四部分&#xff1a;行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习&#xff01;行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想&#xff1a;使多个对象都有机会处…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...

代理篇12|深入理解 Vite中的Proxy接口代理配置

在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...

招商蛇口 | 执笔CID,启幕低密生活新境

作为中国城市生长的力量&#xff0c;招商蛇口以“美好生活承载者”为使命&#xff0c;深耕全球111座城市&#xff0c;以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子&#xff0c;招商蛇口始终与城市发展同频共振&#xff0c;以建筑诠释对土地与生活的…...

vulnyx Blogger writeup

信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面&#xff0c;gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress&#xff0c;说明目标所使用的cms是wordpress&#xff0c;访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...

在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)

考察一般的三次多项式&#xff0c;以r为参数&#xff1a; p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]&#xff1b; 此多项式的根为&#xff1a; 尽管看起来这个多项式是特殊的&#xff0c;其实一般的三次多项式都是可以通过线性变换化为这个形式…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用

一、方案背景​ 在现代生产与生活场景中&#xff0c;如工厂高危作业区、医院手术室、公共场景等&#xff0c;人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式&#xff0c;存在效率低、覆盖面不足、判断主观性强等问题&#xff0c;难以满足对人员打手机行为精…...