性能优化:JIT即时编译与AOT提前编译
优质博文:IT-BLOG-CN
一、简介
JIT
与AOT
的区别: 两种不同的编译方式,主要区别在于是否处于运行时进行编译。
JIT:Just-in-time
动态(即时)编译,边运行边编译:在程序运行时,根据算法计算出热点代码,然后进行JIT
实时编译,这种方式吞吐量高,有运行时性能加成,可以跑得更快,并可以做到动态生成代码等,但是相对启动速度较慢,并需要一定时间和调用频率才能触发JIT
的分层机制。JIT
缺点就是编译需要占用运行时资源,会导致进程卡顿。
AOT:Ahead Of Time
指运行前编译,预先编译:AOT
编译能直接将源代码转化为机器码,内存占用低,启动速度快。无需runtime
运行,直接将runtime
静态链接至最终的程序中,但无运行时性能加成,不能根据程序运行情况做进一步的优化,AOT
缺点就是在程序运行前编译会使程序安装的时间增加。
JIT
即时编译指的是在程序的运行过程中,将字节码转换为可在硬件上直接运行的机器码,并部署至托管环境中的过程。而AOT
编译指的则是,在程序运行之前,便将字节码转换为机器码的过程。
.java -> .class -> (使用jaotc编译工具) -> .so(程序函数库,即编译好的可以供其他程序使用的代码和数据)
二、JIT
在HotSpot
虚拟机中,内置了两种JIT
,分别为C1
编译器和C2
编译器,这两个编译器的编译过程是不一样的。
【1】C1
编译器:C1
编译器是一个简单快速的编译器,主要的关注点在于局部性的优化,适用于执行时间较短或对启动性能有要求的程序,也称为Client Compiler
。
【2】C2
编译器:C2
编译器是为长期运行的服务器端应用程序做性能调优的编译器,适用于执行时间较长或对峰值性能有要求的程序,也称为Server Compiler
,例如,服务器上长期运行的Java
应用对稳定运行就有一定的要求。JDK 6
开始定义服务器级别的机器是至少有两个CPU
和2GB
的物理内存,才开启C2
;
【3】分层编译: 在Java8
中,默认开启分层编译,在1.8
之前,分层编译默认是关闭的。在Java7
之前,需要根据程序的特性来选择对应的JIT
,虚拟机默认采用解释器和其中一个编译器配合工作。
分层编译将JVM
的执行状态分为了5个层次:
【1】第 0 层:程序解释执行,默认开启性能监控功能Profiling
,如果不开启,可触发第二层编译;
【2】第 1 层:可称为C1
编译,将字节码编译为本地代码,进行简单、可靠的优化,不开启Profiling
;
【3】第 2 层:也称为C1
编译,开启Profiling
,仅执行带方法调用次数和循环回边执行次数profiling
的C1
编译;
【4】第 3 层:也称为C1
编译,执行所有带Profiling
的C1
编译;
【5】第 4 层:可称为C2
编译,也是将字节码编译为本地代码,但是会启用一些编译耗时较长的优化,甚至会根据性能监控信息进行一些不可靠的激进优化。
mixed mode
代表是默认的混合编译模式,除了这种模式外,我们还可以使用-Xint
参数强制虚拟机运行于只有解释器的编译模式下,这时JIT
完全不介入工作;也可以使用参数-Xcomp
强制虚拟机运行于只有JIT
的编译模式下。如下:
如果只想开启C2
,可以关闭分层编译-XX:-TieredCompilation
,如果只想用C1
,可以在打开分层编译的同时,使用参数:-XX:TieredStopAtLevel=1
C1、C2
和C1+C2
,分别对应client
、server
和分层编译。C1
编译速度快,优化方式比较保守;C2
编译速度慢,优化方式比较激进。C1+C2
在开始阶段采用C1
编译,当代码运行到一定热度之后采用G2
重新编译。
参数-XX:ReservedCodeCacheSize = N
(其中N
是为特定编译器提供的默认值)主要设置热点代码缓存codecache
的大小。如果缓存不够,则JIT
无法继续编译,并且会去优化,比如编译执行改为解释执行,由此,性能会降低。同时,可以通过java -XX:+PrintCodeCache
查看codecache
的使用情况:
C:/Users/Administrator> java -XX:+PrintCodeCache
CodeCache: size=245760Kb used=1165Kb max_used=1165Kb free=244594Kbbounds [0x000000010be1b000, 0x000000010c08b000, 0x00000011ae1b000]total_blobs=293 nmethods=48 adapters=159compilation: enable
相关参数:
参数 | 默认值 | 说明 |
---|---|---|
-XX:InitialCodeCacheSize | 2555904(240M) | 默认的CodeCache 区域大小,单位为字节 |
-XX:ReservedCodeCacheSize | 251658240(240M) | CodeCache 区域的最大值,单位为字节 |
-XX:CodeCacheExpansionSize | 65536(64K) | CodeCache 每次扩展大小,单位为字节 |
-XX:ExitOnFullCodeCache | false | 当CodeCache 区域满了的时候是否退出JVM |
-XX:UseCodeCacheFlushing | false | 是否在关闭JIT 编译前清除CodeCache |
-XX:MinCodeCacheFlushingInterval | 30 | 刷新CodeCache 的最小时间间隔 ,单位为秒 |
-XX:CodeCacheMinimumFreeSpace | 512000 | 当CodeCache 区域的剩余空间小于参数指定的值时停止JIT 编译。剩余的空间不会再用来存放方法的本地代码, 可以存放本地方法适配器代码 |
-XX:CompileThreshold | 10000 | 指定方法在在被JIT 编译前被调用的次数 |
-XX:OnStackReplacePercentage | 140 | 该值为用于计算是否触发OSR(OnStackReplace) 编译的阈值 |
如何判断热点代码
【1】基于采样的热点探测: 主要是虚拟机会周期性的检查各个线程的栈顶,若某个或某些方法经常出现在栈顶,那这个方法就是“热点方法”。优点是实现简单。缺点是很难精确一个方法的热度,容易受到线程阻塞或外界因素的影响。
【2】基于计数器的热点探测(典型应用-Hotspot): 主要就是虚拟机给每一个方法甚至代码块建立了一个计数器,统计方法的执行次数,超过一定的阀值则标记为此方法为热点方法。Hotspot
使用的基于计数器的热点探测方法。然后使用了两类计数器:方法调用计数器和回边计数器。当方法计数器和回边计数器之和超过方法计数器阈值时,就会触发JIT
编译器。
【3】方法调用计数器: 方法调用计数器用于统计方法被调用的次数,默认阈值在C1
模式下是1500
次,在C2
模式在是10000
次,可通过-XX: CompileThreshold
来设定;而在分层编译的情况下-XX: CompileThreshold
指定的阈值将失效,此时将会根据当前待编译的方法数以及编译线程数来动态调整。
【4】回边计数器: 回边计数器用于统计一个方法中循环体代码执行的次数,在字节码中遇到控制流向后跳转的指令称为“回边”Back Edge
,该值用于计算是否触发C1
编译的阈值,在不开启分层编译的情况下,C1
默认为13995
,C2
默认为10700
,可通过-XX: OnStackReplacePercentage=N
来设置;而在分层编译的情况下,-XX: OnStackReplacePercentage
指定的阈值同样会失效,此时将根据当前待编译的方法数以及编译线程数来动态调整。
回边计数器阈值计算规则:
1、C1
模式下:CompileThreshold*OnStackReplacePercentage/100
;即:方法调用计数器阈值*OSR
比率/100
;
2、C2
模式下:(CompileThreshold)*(OnStackReplacePercentage-InterpreterProfilePercentage)/100
;即:方法调用计数器阈值*(OSR
比率 - 解释器监控比率)/100
;
JIT优化
JIT
编译运用了一些经典的编译优化技术来实现代码的优化:主要有两种:方法内联和逃逸分析。
【1】方法内联: 方法内联的优化行为就是把目标方法的代码复制到发起调用的方法之中,避免发生真实的方法调用。方法内联不仅可以消除调用本身带来的性能开销,还可以进一步触发更多的优化。
private int add1(int s1, int s2, int s3, int s4) {return add2(s1+s2) + add2(s3+s4);}private int add2(int s1, int s2) {return s1+s2;} // 方法内联后的代码private int add(int s1, int s2, int s3, int s4) {return s1+s2+s3+s4;}
提高方法内联的策略:调整参数;写小方法;使用static
、final
关键字,不用出现方法继承,没有额外的类型检查,就可能发生内联;
【2】锁消除: 如果是在单线程环境下,JIT
编译会对这个对象的方法锁进行锁消除,jdk1.8
默认开启。例如:
//-XX:-EliminateLocks 先关闭锁消除, 再打开, 执行此段代码100万次查看差别很大public static String getString(String s1, String s2) {StringBuffer sb = new StringBuffer();sb.append(s1);sb.append(s2);return sb.toString();}
【3】标量替换: 逃逸分析证明一个对象不会被外部访问,如果这个对象可以被拆分的话,当程序真正执行的时候可能不创建这个对象,可以直接创建它的成员变量来代替,前提要开启逃逸分析(jdk1.8
默认开启逃逸分析-XX:+DoEscapeAnalysis
; -XX:+EliminateAllocations
开启标量替换jdk1.8
默认开启)。
public void foo() {Person info = new Person ();info.name = "queen";info.age= 18;
}//逃逸分析后,代码会被优化(标量替换)为:
public void foo() {String name= "queen";int age= 18;
}
三、AOT
优点: Java
虚拟机加载已经预编译成二进制库,可以直接执行。不必等待及时编译器的预热,减少Java
应用给人带来“第一次运行慢” 的不良体验。在程序运行前编译,可以避免在运行时的编译性能消耗和内存消耗。可以在程序运行初期就达到最高性能,程序启动速度快。运行产物只有机器码,打包体积小。
缺点: 由于是静态提前编译,不能根据硬件情况或程序运行情况择优选择机器指令序列,理论峰值性能不如JIT
,没有动态能力,同一份产物不能跨平台运行。第一种即时编译JIT
是默认模式,Java Hotspot
虚拟机使用它在运行时将字节码转换为机器码。后者提前编译AOT
由新颖的GraalVM
编译器支持,并允许在构建时将字节码直接静态编译为机器码。
现在正处于云原生,降本增效的时代,Java
相比于Go
、Rust
等其他编程语言非常大的弊端就是启动编译和启动进程非常慢,这对于根据实时计算资源,弹性扩缩容的云原生技术相冲突,Spring6
借助AOT
技术在运行时内存占用低,启动速度快,逐渐的来满足Java
在云原生时代的需求,对于大规模使用Java
应用的商业公司可以考虑尽早调研使用JDK17
,通过云原生技术为公司实现降本增效。
Graalvm
Spring6
支持的AOT
技术,底层通过GraalVM
支持,Spring
也对GraalVM
本机映像提供了一流的支持。GraalVM
是一种高性能JDK
,旨在加速用Java
和其他JVM
语言编写的应用程序的执行,同时还为JavaScript
、Python
和许多其他流行语言提供运行时。GraalVM
提供两种运行 Java
应用程序的方法:在HotSpot JVM
上使用Graal
即时JIT
编译器或作为提前AOT
编译的本机可执行文件。GraalVM
的多语言能力使得在单个应用程序中混合多种编程语言成为可能,同时消除了外部调用成本。GraalVM
向HotSpot Java
虚拟机添加了一个用Java
编写的高级即时JIT
优化编译器。
GraalVM
具有以下特性:
【1】一种高级优化编译器,它生成更快、更精简的代码,需要更少的计算资源;
【2】AOT
本机图像编译提前将Java
应用程序编译为本机二进制文件,立即启动,无需预热即可实现最高性能;
【3】Polyglot
编程在单个应用程序中利用流行语言的最佳功能和库,无需额外开销;
【4】高级工具在Java
和多种语言中调试、监视、分析和优化资源消耗;
Native Image
目前业界除了这种在JVM
中进行AOT
的方案,还有另外一种实现Java AOT
的思路,那就是直接摒弃JVM
,和C/C++
一样通过编译器直接将代码编译成机器代码,然后运行。这无疑是一种直接颠覆Java
语言设计的思路,那就是GraalVM Native Image
。它通过C
语言实现了一个超微缩的运行时组件Substrate VM
,基本实现了JVM
的各种特性,但足够轻量、可以被轻松内嵌,这就让Java
语言和工程摆脱JVM
的限制,能够真正意义上实现和C/C++
一样的AOT
编译。这一方案在经过长时间的优化和积累后,已经拥有非常不错的效果,基本上成为Oracle
官方首推的Java AOT
解决方案。
Native Image
是一项创新技术,可将Java
代码编译成独立的本机可执行文件或本机共享库。在构建本机可执行文件期间处理的Java
字节码包括所有应用程序类、依赖项、第三方依赖库和任何所需的JDK
类。生成的自包含本机可执行文件特定于不需要JVM
的每个单独的操作系统和机器体系结构。
Native Image 构建过程
下载GraalVM 并配置环境变量:将JAVA_HOME
修改为graalvm
的位置,并将Path
修改为graalvm
的bin
位置。
变量名:JAVA_HOME
变量值:D:\graalvm-ce-java17-22.3.0
检查是否安装成功
C:/Users/Administrator>java -version
openjdk version "17.0.5" 2023-10-32
OpenJDK Runtime Environment GraalVM CE 22.3.0 (build 17.0.5+8-jvmci-22.3-b08)
OpenJDK 64-Bit Server VM GraalVM CE 22.3.0 (build 17.0.5+8-jvmci-22.3-b08, mixed mode, sharing)
通过gu install native-image
安装native-image
插件,并通过gu list
检查版本
C:/Users/Administrator>gu install native-image
......
C:/Users/Administrator>gu list
......
native-image 22.3.0 Native Image Early adopter
对比: 通过javac xx.java
和native-image xx
构建文件,其中包含了SVM
和JDK
各种库后的大小,虽然相比C/C++
的二进制文件来说体积偏大,但是对比完整JVM
来说,可以说是已经是非常小了。
相比于使用JVM
运行,Native Image
的速度要快上不少,cpu
占用也更低一些,从官方提供的各类实验数据也可以看出Native Image
对于启动速度和内存占用带来的提升是非常显著的:
相关文章:

性能优化:JIT即时编译与AOT提前编译
优质博文:IT-BLOG-CN 一、简介 JIT与AOT的区别: 两种不同的编译方式,主要区别在于是否处于运行时进行编译。 JIT:Just-in-time动态(即时)编译,边运行边编译:在程序运行时,根据算法计算出热点代码…...

抖音同城榜:探索城市新潮流
随着科技的飞速发展,短视频已经成为了人们日常生活中不可或缺的一部分。作为短视频领域的佼佼者,抖音一直致力于为用户带来更丰富、更有趣的短视频内容。抖音同城榜应运而生,成为了最新、最热门的话题聚集地,吸引了大量潮流达人和…...

云表|低代码开发崛起:重新定义企业级应用开发
低代码开发这个概念在近年来越来越受到人们的关注,市场对于低代码的需求也日益增长。据Gartner预测,到2025年,75%的大型企业将使用至少四种低代码/无代码开发工具,用于IT应用开发和公民开发计划。 那么,为什…...
【算法题】2906. 构造乘积矩阵
题目: 给你一个下标从 0 开始、大小为 n * m 的二维整数矩阵 grid ,定义一个下标从 0 开始、大小为 n * m 的的二维矩阵 p。如果满足以下条件,则称 p 为 grid 的 乘积矩阵 : 对于每个元素 p[i][j] ,它的值等于除了 g…...

机器学习基础之《回归与聚类算法(4)—逻辑回归与二分类(分类算法)》
一、什么是逻辑回归 1、逻辑回归(Logistic Regression)是机器学习中的一种分类模型,逻辑回归是一种分类算法,虽然名字中带有回归,但是它与回归之间有一定的联系。由于算法的简单和高效,在实际中应用非常广…...

UWB安全数据通讯STS-加密、身份认证
DW3000系列才能支持UWB安全数据通讯,DW1000不支持 IEEE 802.15.4a没有数据通讯安全保护机制,IEEE 802.15.4z中指定的扩展得到增强(在PHY/RF级别):增添了一个重要特性“扰频时间戳序列(STS)”&a…...

vue3中去除eslint严格模式
vue3中去除eslint严格模式 1、全局搜索:extends 2、一般在package.json或者vue.config.js中,直接删除掉vue/standard,重启项目。(在package.json文件中,编译不允许有注释,所以直接删掉)...

Win10如何彻底关闭wsappx进程?
Win10如何彻底关闭wsappx进程?在Win10电脑中,用户看到了wsappx进程占用了大量的系统资源,所以想结束wsappx进程,提升电脑的运行速度。但是,用户们不知道彻底关闭掉wsappx进程的方法,那么接下来小编就给大家…...

docker 安装 sftpgo
sftpgo 简介 sftpgo 是一个功能齐全且高度可配置的 SFTP 服务器,具有可选的 HTTP/S、FTP/S 和 WebDAV 支持。支持多种存储后端:本地文件系统、加密本地文件系统、S3(兼容)对象存储、Google 云存储、Azure Blob 存储、SFTP。 官…...
threejs (一) 创建一个场景
引入 npm install three import * as THREE from three;const scene new THREE.Scene();或者使用bootCDN复制对应的版本连接 <script src"https://cdn.bootcdn.net/ajax/libs/three.js/0.156.1/three.js"></script>基础知识 场景、相机、渲染器 通过…...

二分查找,求方程多解
1.暴力遍历: 解为两位小数,故0.001的范围肯定可以包含(零点存在) 2.均分为区间长度为1的小区间(由于两解,距离不小于1),一个区间最多一个解 1.防止两边端点都为解 2&…...

代码随想录算法训练营第二十九天 | 回溯算法总结
代码随想录算法训练营第二十九天 | 回溯算法总结 1. 组合问题 1.1 组合问题 在77. 组合中,我们开始用回溯法解决第一道题目:组合问题。 回溯算法跟k层for循环同样是暴力解法,为什么用回溯呢?回溯法的魅力,用递…...

运算方法和运算电路
一、逻辑门电路 1、逻辑门电路基础总结 2、异或运算妙用 3、逻辑常用公式 二、加法器(重点) 1、标志位的生成原理 2、加法器总结 三、多路门选择器,三态门...

计算机网络篇之TCP滑动窗口
文章目录 前言概述 前言 在网络数据传输时,若传输的原始数据包比较大,会将数据包分解成多个数据包进行发送。需要对数据包确认后,才能发送下一个数据包。在等待确认包的这个过程浪费了大量的时间,不过还好TCP引入了滑动窗口的概念…...

本地安装telepresence,访问K8S集群 Mac(m1) 非管理員
kubeconfig 一.安装telepresence 1.安装 Telepresence Quickstart | Telepresence (1)brew install datawire/blackbird/telepresence 2.配置 目录kubectl 将使用默认的 kubeconfig 文件:$HOME/.kube/config 创建文件夹&…...
今日思考(2) — 训练机器学习模型用GPU还是NUP更有优势(基于文心一言的回答)
前言 深度学习用GPU,强化学习用NPU。 1.训练深度学习模型,强化学习模型用NPU还是GPU更有优势 在训练深度学习模型时,GPU相比NPU有优势。GPU拥有更高的访存速度和更高的浮点运算能力,因此更适合深度学习中的大量训练数据、大量矩阵…...
8.3 C++ 定义并使用类
C/C语言是一种通用的编程语言,具有高效、灵活和可移植等特点。C语言主要用于系统编程,如操作系统、编译器、数据库等;C语言是C语言的扩展,增加了面向对象编程的特性,适用于大型软件系统、图形用户界面、嵌入式系统等。…...

Git学习笔记——超详细
Git笔记 安装git: apt install git 创建版本库: git init 添加文件到版本库: git add 文件 提交文件到仓库: git commit -m “注释” 查看仓库当前的状态信息: git status 查看修改内容和之前版本的区别&am…...

Locust负载测试工具实操
本中介绍如何使用Locust为开发的服务/网站执行负载测试。 Locust 是一个开源负载测试工具,可以通过 Python 代码构造来定义用户行为,避免混乱的 UI 和臃肿的 XML 配置。 步骤 设置Locust。 在简单的 HTTP 服务上模拟基本负载测试。 准备条件 Python…...

关闭mysql,关闭redis服务
1. 关闭redis服务: 查询redis安装目录: whereis redis which redis find / -name redis 关闭redis服务: redis-cli -h 127.0.0.1 -p 6379 auth 输入密码 shutdown 关闭redis服务 2. 关闭mysql服务: 查询mysql安装目录&…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...

python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...