性能优化:JIT即时编译与AOT提前编译

优质博文:IT-BLOG-CN

一、简介

JITAOT的区别: 两种不同的编译方式,主要区别在于是否处于运行时进行编译。

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开始定义服务器级别的机器是至少有两个CPU2GB的物理内存,才开启C2
【3】分层编译:Java8中,默认开启分层编译,在1.8之前,分层编译默认是关闭的。在Java7之前,需要根据程序的特性来选择对应的JIT,虚拟机默认采用解释器和其中一个编译器配合工作。

分层编译将JVM的执行状态分为了5个层次:
【1】第 0 层:程序解释执行,默认开启性能监控功能Profiling,如果不开启,可触发第二层编译;
【2】第 1 层:可称为C1编译,将字节码编译为本地代码,进行简单、可靠的优化,不开启Profiling
【3】第 2 层:也称为C1编译,开启Profiling,仅执行带方法调用次数和循环回边执行次数profilingC1编译;
【4】第 3 层:也称为C1编译,执行所有带ProfilingC1编译;
【5】第 4 层:可称为C2编译,也是将字节码编译为本地代码,但是会启用一些编译耗时较长的优化,甚至会根据性能监控信息进行一些不可靠的激进优化。

mixed mode代表是默认的混合编译模式,除了这种模式外,我们还可以使用-Xint参数强制虚拟机运行于只有解释器的编译模式下,这时JIT完全不介入工作;也可以使用参数-Xcomp强制虚拟机运行于只有JIT的编译模式下。如下:

如果只想开启C2,可以关闭分层编译-XX:-TieredCompilation,如果只想用C1,可以在打开分层编译的同时,使用参数:-XX:TieredStopAtLevel=1

C1、C2C1+C2,分别对应clientserver和分层编译。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:InitialCodeCacheSize2555904(240M)默认的CodeCache区域大小,单位为字节
-XX:ReservedCodeCacheSize251658240(240M)CodeCache区域的最大值,单位为字节
-XX:CodeCacheExpansionSize65536(64K)CodeCache每次扩展大小,单位为字节
-XX:ExitOnFullCodeCachefalseCodeCache区域满了的时候是否退出JVM
-XX:UseCodeCacheFlushingfalse是否在关闭JIT编译前清除CodeCache
-XX:MinCodeCacheFlushingInterval30刷新CodeCache的最小时间间隔 ,单位为秒
-XX:CodeCacheMinimumFreeSpace512000CodeCache区域的剩余空间小于参数指定的值时停止JIT编译。剩余的空间不会再用来存放方法的本地代码, 可以存放本地方法适配器代码
-XX:CompileThreshold10000指定方法在在被JIT编译前被调用的次数
-XX:OnStackReplacePercentage140该值为用于计算是否触发OSR(OnStackReplace)编译的阈值

如何判断热点代码

【1】基于采样的热点探测: 主要是虚拟机会周期性的检查各个线程的栈顶,若某个或某些方法经常出现在栈顶,那这个方法就是“热点方法”。优点是实现简单。缺点是很难精确一个方法的热度,容易受到线程阻塞或外界因素的影响。
【2】基于计数器的热点探测(典型应用-Hotspot): 主要就是虚拟机给每一个方法甚至代码块建立了一个计数器,统计方法的执行次数,超过一定的阀值则标记为此方法为热点方法。Hotspot使用的基于计数器的热点探测方法。然后使用了两类计数器:方法调用计数器和回边计数器。当方法计数器和回边计数器之和超过方法计数器阈值时,就会触发JIT编译器。
【3】方法调用计数器: 方法调用计数器用于统计方法被调用的次数,默认阈值在C1模式下是1500次,在C2模式在是10000次,可通过-XX: CompileThreshold来设定;而在分层编译的情况下-XX: CompileThreshold指定的阈值将失效,此时将会根据当前待编译的方法数以及编译线程数来动态调整。
【4】回边计数器: 回边计数器用于统计一个方法中循环体代码执行的次数,在字节码中遇到控制流向后跳转的指令称为“回边”Back Edge,该值用于计算是否触发C1编译的阈值,在不开启分层编译的情况下,C1默认为13995C2默认为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;}

提高方法内联的策略:调整参数;写小方法;使用staticfinal关键字,不用出现方法继承,没有额外的类型检查,就可能发生内联;

【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相比于GoRust等其他编程语言非常大的弊端就是启动编译和启动进程非常慢,这对于根据实时计算资源,弹性扩缩容的云原生技术相冲突,Spring6借助AOT技术在运行时内存占用低,启动速度快,逐渐的来满足Java在云原生时代的需求,对于大规模使用Java应用的商业公司可以考虑尽早调研使用JDK17,通过云原生技术为公司实现降本增效。

Graalvm

Spring6支持的AOT技术,底层通过GraalVM支持,Spring也对GraalVM本机映像提供了一流的支持。GraalVM是一种高性能JDK,旨在加速用Java和其他JVM语言编写的应用程序的执行,同时还为JavaScriptPython和许多其他流行语言提供运行时。GraalVM提供两种运行 Java应用程序的方法:在HotSpot JVM上使用Graal即时JIT编译器或作为提前AOT编译的本机可执行文件。GraalVM的多语言能力使得在单个应用程序中混合多种编程语言成为可能,同时消除了外部调用成本。GraalVMHotSpot 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修改为graalvmbin位置。

变量名: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.javanative-image xx构建文件,其中包含了SVMJDK各种库后的大小,虽然相比C/C++的二进制文件来说体积偏大,但是对比完整JVM来说,可以说是已经是非常小了。

相比于使用JVM运行,Native Image的速度要快上不少,cpu占用也更低一些,从官方提供的各类实验数据也可以看出Native Image对于启动速度和内存占用带来的提升是非常显著的:

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

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

相关文章

类图表示法

设计模式,用设计图表示的话,主要用到类图。常见UML类图如下: 1、类图:矩形框,代表一个类(Class)。类图分为三层,第一层显示类的名称,如果是抽象类,则用斜体显…

FFmpeg编译安装(windows环境)以及在vs2022中调用

文章目录 下载源码环境准备下载msys换源下载依赖源码位置 开始编译编译x264编译ffmpeg 在VS2022写cpp调用ffmpeg 下载源码 直接在官网下载压缩包 这个应该是目前(2023/10/24)最新的一个版本。下载之后是这个样子: 我打算添加外部依赖x264&a…

探索随机森林: 机器学习中的集成学习神器

机器学习 第七课 随机森林 概述机器学习机器学习的主要分类监督学习无监督学习强化学习 集成学习提高准确性增强稳定性提升泛化能力 集成学习的主要方法BaggingBoostingStacking 随机森林的理论基础决策树的基本原理随机森林的生成过程随机森林的优势与局限性 随机森林的实际应…

进阶JAVA篇-深入了解 Stream 流对象的创建与中间方法、终结方法

目录 1.0 Stream 流的说明 2.0 Stream 流对象的创建 2.1 对于 Collection 系列集合创建 Stream 流对象的方式 2.2 对于 Map 系列集合创建 Stream 流对象的方式 2.3 对于数组创建 Stream 流对象的方式 3.0 Stream 流的中间方法 3.1 Stream 流的 filter() 中间方法 3.2 Stream 流…

C++基类和派生类的内存分配,多态的实现

目录 基类和派生类的内存分配基类和派生类的成员归属多态的实现 基类和派生类的内存分配 类包括成员变量(data member)和成员函数(member function)。 成员变量分为静态数据(static data)和非静态数据&…

Git简明教程

1.Git的定位 在我们自己开发项目的过程中,经常会遇到这样的情况,为了防止代码丢失,或者新变更的代码影响到原有的代码功能,为了在失误后能恢复到原来的版本,不得不复制出一个副本,比如:“坦克大战1.0”“坦…

【Html】交通灯问题

效果 实现方式 计时器:setTimeout或setInterval来计时。setInterval和 setTimeout 在某些情况下可能会出现计时不准确的情况。这通常是由于JavaScript的事件循环机制和其他代码执行所需的时间造成的。 问询:通过getCurrentLight将每个状态的持续时间设置…

Git常用的命令有哪些?

一、前言 git 的操作可以通过命令的形式如执行,日常使用就如下图6个命令即可 实际上,如果想要熟练使用,超过60多个命令需要了解,下面则介绍下常见的的git 命令 二、有哪些 配置 Git 自带一个 git config 的工具来帮助设置控制…

Haproxy 服务

Haproxy:他也是常用的负载均衡软件 nginx 支持四层转发,七层转发 haproxy 也是四层和七层转发 LVS的DR和NAT都是基于四层转发 都是基于流量的转发。 tun:四层和七层都有。 基于四层的转发: 1,lvs 2,nginx 3&…

对python中切片详解

嗨喽,大家好呀~这里是爱看美女的茜茜呐 Python中什么可以切片 Python中符合序列的有序序列都支持切片(slice) 如:列表,字符,元祖 👇 👇 👇 更多精彩机密、教程,尽在下方,赶紧点击了解吧~ python源码、视…

“唯品会VIP商品API:一键获取奢侈品详情,尊享品质生活!“

要获取唯品会VIP商品的详细信息,您可以通过唯品会的API接口进行调用。 唯品会提供了多种商品选择,包括服装、美容护肤、鞋子、箱包、家居、母婴等等。在这些商品中,VIP奢侈品专区是唯品会的重要特色之一。 要获取VIP商品的详细信息&#xf…

计算机网络-计算机网络体系结构-应用层

目录 一、网络应用模型 客户/服务器模型(Client/Server) P2P模型(Peer-to-peer) 二、域名解析系统(DNS) 域名 域名服务器 解析过程 三、文件传输协议(FTP) FTP控制原理 四、电子邮件 组成结构 协议 SMTP MIME POP3 IMAP 五、万维网和HTTP协议 概述 HTTP 报…

程桌面管理软件Apple Remote Desktop mac中文介绍说明

Apple Remote Desktop mac是一款远程桌面管理软件。它可以让用户通过局域网或互联网连接到其他远程计算机,并实时监控和管理这些计算机。 使用Apple Remote Desktop,用户可以轻松远程操作和控制其他计算机的桌面。用户可以在远程计算机上查看、操控和键入…

WPF Material Design UI框架

前言 Material Design in xaml 是开源免费的ui框架&#xff0c;工控软件主打的就是简单界面。 以下简称MD 相关资源 MaterialDesignInXamlToolkit Github 地址 MD 快速启动 MD 案例压缩包 MD 框架使用 启动环境配置 安装Nuget包 App.xaml 配置 <Application x:Class&qu…

我用GPT4 预测了10年后中国大学排名Top10

目录 前言 &#x1f3eb;预测大学排名 &#x1f525; GPT4 预测排名的理由 最后 &#x1f388;个人主页&#xff1a;库库的里昂 &#x1f390;C/C领域新星创作者 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏✨收录专栏&#xff1a;杂谈&#x1f91d;希望作者的文章能…

Java IDEA feign调用上传文件MultipartFile以及实体对象亲测可行

Java IDEA feign调用上传文件MultipartFile以及实体对象亲测可行 1. 报错 java.lang.IllegalStateException: Body parameter cannot be used with form parameters2. 解决参考 1. 报错 java.lang.IllegalStateException: Body parameter cannot be used with form parameters …

海思专业级图像分析处理边缘计算盒子,双核A55 64位处理器+2.5TOPS算力

随着大模型、物联网等技术的快速发展&#xff0c;边缘计算逐渐成为了当今信息技术领域的研究热点&#xff0c;边缘算力的重要性愈加凸显。据市场需求反馈&#xff0c;大量长尾场景普遍具有小型化、灵活多变、即时性等特点&#xff0c;更需要关注算力的利用率、以及方案的成本、…

macOS telnet替代方式

前言 经过使用Linux&#xff0c;常常用Linux的telnet查看端口畅通&#xff0c;是否有防火墙&#xff0c;但是在mac上已经没有这个命令了&#xff0c;那么怎么使用这个命令或者有没有其他替代呢&#xff0c;win和linux是否可以使用相同的替代。macOS可以原生用nc命令替代&#…

公司电脑屏幕录制软件有什么功能

电脑屏幕录制软件有很多&#xff0c;今天简单说说说它的基础功能和附属功能&#xff1a; 基础功能&#xff1a; 1、屏幕录像 支持对所选电脑的屏幕进行录制&#xff0c;并且支持调整截屏频度、画面质量、单个视频时长等。 2、实时屏幕 可以对对方电脑进行实时屏幕查看&…

深入探究Selenium定位技巧及最佳实践

在使用Selenium进行Web自动化测试时&#xff0c;准确地定位元素是非常重要的一步。Selenium提供了多种元素定位方法&#xff0c;本文将深入探究这八大元素定位方法&#xff0c;帮助读者更好地理解和应用Selenium的定位技巧。 1. ID定位 ID是元素在HTML中的唯一标识符&#xff…