即时编译器JIT
类编译加载执行过程
如下图所示,一个Java
代码从编译到运行大抵会经历以下几个过程。具体每个过程笔者会在下文站展开讨论。
类编译
首先是类编译阶段,这个阶段会将Java
文件变为class
文件,这个class
文件包含一个常量池和方法表集合,而方法表集合里面会包含方法访问权限、返回类型、JVM
执行指令以及属性集合等信息。
类加载
对于没有加载的类,JVM
就会拿着这个class
文件进行类加载,JDK
自带的本地方法在双亲委派机制下,会用根加载器(Bootstrp loader)
进行加载,而JDK
扩展方法则会由扩展加载器(ExtClassLoader )
,我们应用程序自己写的方法则是由系统加载器(AppClassLoader )
完成加载。
完成加载后,常量池或者每个类的字段描述符、方法描述符等信息都会加载到JVM
的方法区,同时会在堆区生成一个代表这个类的java.lang.Class
对象,作为这个类的各种数据的访问入口。
类连接
类连接就是验证、准备、初始化3个过程了。
验证
:验证类符合 Java 规范和 JVM 规范,在保证符合规范的前提下,避免危害虚拟机安全。准备
: 为类的静态变量分配内存,初始化为系统初始值。private final static int value=123
,在这一步就完成空间分配和初始赋值为0。而private final int num=123
则会在这一步直接赋值为123
。因为它是一个常量。解析
:将编译器每个类的符号引用(包括类和接口的全限定名、类引用、方法引用以及成员变量引用等)
等信息转为直接引用(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
编译器,分别为:
C1编译器:
主要关注点在于局部性优化,常用于那些执行时间短,或者要求快速启动的应用程序,例如GUI应用程序。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的阈值)
以外,还要达到以下要求:
- 对于经常执行的方法,方法体要小于
325
字节,这个字节数可以通过-XX:MaxFreqInlineSize=N
来调整。 - 对于不经常执行的方法,方法体要小于
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
优化了
所以这就要求我们平时写代码时:
- 方法体尽可能小
- 尽可能使用
private
、final
、static
修饰,避免一些没必要的类是否继承等相关检查。
栈上分配
在将栈上分配前,我们需要先了解一个叫逃逸分析(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
也会实现这项优化功能。
锁消除
同样在逃逸分析某些没有被外部方法或者其他线程引用的情况下,会将某些锁消除。例如下面这段代码,实际上你在运行时可以发现StringBuffer
和StringBuilder
性能上没有什么区别,这正是因为锁消除为我们做的优化工作。
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
类编译加载执行过程 如下图所示,一个Java代码从编译到运行大抵会经历以下几个过程。具体每个过程笔者会在下文站展开讨论。 类编译 首先是类编译阶段,这个阶段会将Java文件变为class文件,这个class文件包含一个常量池和方法表集合…...

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类模板;类模板的写法和一般类的写法有很大的差别;不容易熟悉;下面再做一遍; 做一个椭圆类,成员有长轴长度和短轴长度; // ellipse.h: interface for the ellipse class. // //#if !define…...

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

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的期货预测,利用PSO优化SVM和未优化的SVM进行对比,得到实际输出和期望输出结果。线性核函数、多项式、RBF核函数三种核函数任意可选,并给出均方根误差,相对误差等结果,程序已调通,可直接运行。 3…...

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

sql第二次上机作业
1查找借阅了ISBN为“4-6045-1023-4”的借书证号,读者姓名,专业名和借书时间 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(列表)
目录 描述 输入描述: 输出描述: 解答 : 说明: 描述 某公司在面试结束后,创建了一个依次包含字符串 Allen 和 Tom 的列表offer_list,作为通过面试的名单。 请你依次对列表中的名字发送类似 Allen, you…...

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

C/C++晶晶赴约会 2020年12月电子学会青少年软件编程(C/C++)等级考试一级真题答案解析
目录 C/C晶晶赴约会 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 C/C晶晶赴约会 2020年12月 C/C编程等级考试一级编程题 一、题目要求 1、编程实现 晶晶的朋友贝贝约晶晶下周一起去看展览࿰…...

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

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

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

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

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

如何使用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不同的是,要用 action 来配置后端上传图片的接口地址; 再来一些配置项的命名有所不同,一般1.x的命名用 -,2.x的命名使用小驼峰; 1.x 的上传会自带删除时的提示框,2.x 没有; 重要的几个配置…...

基于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技术可行性:…...

软件测试常用的8种功能测试类型有哪些?
软件测试常用的8种功能测试类型有哪些? 单元测试 单元测试确保在一个段中编写的每一段代码都能产生最佳结果。开发人员在单元测试期间只看接口和确定部件。它提供了代码进展的文档,因为每个代码单元在继续下一个之前都经过了彻底的测试。 集成测试 至少对…...

动态规划之01背包问题
01背包问题 1. 【模板】01背包2. 分割等和子集3. 目标和4. 最后一块石头的重量 II 01背包问题是一种动态规划问题,用于求解在有限容量的背包中装入最大价值的物品组合。具体步骤如下: 定义一个二维数组dp[i][j],表示从前i个物品中选择若干个…...

安防监控项目---boa服务器的移植
文章目录 前言一、boa服务器简介二、移植步骤三、测试结果四、A9平台移植BOA总结 前言 书接上期,在配置完成环境后,那么接下来呢还得移植两个非常关键的东西,一个呢时boa服务器,另一个呢时cgi接口,boa服务器能够使得我…...

Gson 字符串常用转换方式(集合转换为Json数组
数组转换为 Json 字符串: GsonUtils.toJson(itemListBean.getImgs())json 字符串转换为数组 Gson().fromJson(goodsDbBean.getImgs(), String[].class)Json 转换为已知实体类 GsonUtils.parseJSON(result, AFileInfoBean.class);Json 转换为已知实体类集合 List<…...

MyBatis的使用(XML映射文件)
MyBatis的使用(XML映射文件) MyBatis基于注解开发简单便捷,但是弊端是失去SQL语句的灵活性,不能根据实际情况产生不同的SQL语句 MyBatis除了支持注解开发以外,还支持一种开发方式:XML映射文件,…...

localhost知识
文章目录 一、localhost是什么?二、localhost 在平时用到的地方三、 localhost 与 127.0.01 一、localhost是什么? localhost 是一个特殊的主机名,通常指代本机。它被用来进行本地开发和测试,也常被用作网络配置中的占位符&#…...

PyTorch入门学习(八):神经网络-卷积层
目录 一、数据准备 二、创建卷积神经网络模型 三、可视化卷积前后的图像 一、数据准备 首先,需要准备一个数据集来演示卷积层的应用。在这个示例中,使用了CIFAR-10数据集,该数据集包含了10个不同类别的图像数据,用于分类任务。…...

【EI会议征稿】 2024年遥感、测绘与图像处理国际学术会议(RSMIP2024)
2024年遥感、测绘与图像处理国际学术会议(RSMIP2024) 2024 International Conference on Remote Sensing, Mapping and Image Processing 2024年遥感、测绘与图像处理国际学术会议(RSMIP2024)将于2024年1月19日-21日在中国厦门举行。会议主要围绕遥感、测绘与图像处理等研究领…...

MySQL 8 - 处理 NULL 值 - is null、=null、is not null、<> null 、!= null
处理 NULL 值: IS NULL:IS NULL 用于检查一个列是否为 NULL。例如,如果查找一个表中某一列的值为 NULL 的行,可以使用以下语法: SELECT * FROM table_name WHERE column_name IS NULL;IS NOT NULL:IS NOT N…...

高教社杯数模竞赛特辑论文篇-2018年C题:大型百货商场会员画像描述(附获奖论文及MATLAB代码实现)
目录 摘 要 一、问题重述 1.1 问题背景 1.2 问题提出 二、问题分析 2.1 问题一的分析...