Jvm之JIT优化详细解释

文章目录

  • 一、JIT 产生的背景
  • 二、HotSpot虚拟机内置JIT编译器
    • 1. Client Compiler
    • 2. Server Compiler
    • 3. 查看本地编译器模式
  • 三、常见热点探测技术
    • 1. 基于计数器的热点探测
    • 2. 基于采样的热点探测
      • 2.1 方法调用计数器
      • 2.2 回边计数器
  • 四、常见JIT优化手段
    • 1. 公共子表达式消除
    • 2. 方法内联
    • 3. 逃逸分析
      • 3.1 逃逸分析之标量替换
      • 3.2 逃逸分析之栈上分配
      • 3.3 逃逸分析之同步消除
  • 五、JIT优化可能引发的问题
    • 1. 提升JIT优化的效率
    • 2. 降低瞬时请求量

一、JIT 产生的背景

我们知道,将高级语言转换成计算机可识别的机器语言有两种方式,即编译和解释。尽管在Java中,代码需要编译成字节码才能执行,但字节码本身并不能直接在机器上执行。

因此,JVM内置了解释器(interpreter),在运行时对字节码进行解释,将其翻译成机器码,然后执行。

解释器的执行方式是边翻译边执行,因此执行效率较低。为了解决这个低效问题,HotSpot引入了JIT技术(即时编译)。

有了JIT技术之后,JVM仍然使用解释器进行解释执行。但是,当JVM发现某个方法或代码块在运行时频繁执行时,会将其标记为"热点代码"。然后JIT将部分热点代码翻译成本地机器相关的机器码,并进行优化,再将翻译后的机器码缓存起来供下次使用。

JVM之JIT优化

二、HotSpot虚拟机内置JIT编译器

HotSpot虚拟机内置了两个JIT编译器:Client Compiler和Server Compiler, 分别用于客户端和服务器端。在当前主流的HotSpot虚拟机中,默认采用解释器与其中一个编译器直接配合的方式工作。

当JVM执行代码时,并不会立即开始编译代码。首先,如果代码只会被执行一次,那么编译代码相对于将代码翻译成Java字节码来说是一种浪费。因为将代码翻译成字节码的过程比编译和执行代码的过程要快很多。其次,JVM在编译代码时会进行优化。当某个方法或循环被执行的次数越多,JVM就会对代码结构有更深入的了解,并在编译代码时进行相应的优化。

1. Client Compiler

Client Compiler(也称为C1编译器或Client JIT)主要优化启动速度和内存占用。它会在程序运行初期进行编译,以快速生成可执行代码,但对于性能优化的程度较低。

2. Server Compiler

Server Compiler(也称为C2编译器或Server JIT)则注重在运行时对代码进行更深层次的优化,以提高程序的执行效率。它会在程序运行过程中,通过动态分析和优化来生成高性能的机器码。

3. 查看本地编译器模式

如果想要查看机器上安装的JDK中JIT采用的是哪种模式,可以执行"java -version"命令。这个命令将显示JDK的版本信息,其中也包括了JIT编译器的模式。

java -version

编译器模式
图中显示的是自己本地机器上安装的JDK 1.8,并且 JIT 编译器的模式是Server Compiler。 然而,需要指出的是,无论是Client Compiler还是Server Compiler,解释器与编译器都是以混合模式配合使用的,即图中显示的是mixed mode。

三、常见热点探测技术

为了触发JIT编译,首先需要识别出热点代码。目前,主要使用热点探测(Hot Spot Detection)来实现热点代码的识别,其中有两种常见的方式。

1. 基于计数器的热点探测

其中一种是基于计数器的热点探测,通过对方法的调用次数进行计数,当达到一定阈值时,将该方法标记为热点代码。这种方式简单直接,适用于识别一些简单的热点场景。

2. 基于采样的热点探测

HotSpot虚拟机使用周期性检测各个线程的栈顶的方法来判断热点方法。如果某个方法经常出现在栈顶,就被认为是热点方法。这种方法的好处在于简单易懂,但缺点是无法精确确定一个方法的热度。此外,它容易受到线程阻塞或其他原因的干扰,从而影响热点探测的准确性。

在HotSpot虚拟机中,使用的是基于计数器的热点探测方法,因此为每个方法准备了两个计数器:方法调用计数器和回边计数器。

2.1 方法调用计数器

方法调用计数器,顾名思义,是用来记录一个方法被调用的次数的计数器。它会统计方法的调用次数,并当达到一定阈值时将该方法标记为热点代码。

2.2 回边计数器

回边计数器则是用来记录方法中的循环结构(如for或while循环)的运行次数的计数器。它会统计循环结构的迭代次数,通过迭代次数的多少来判断循环是否是热点代码。

这两个计数器的作用是为了帮助HotSpot虚拟机识别热点代码,从而触发JIT编译进行优化。方法调用计数器用于识别频繁调用的方法,回边计数器用于识别运行次数较多的循环结构。通过对这些热点代码的识别,可以提高程序的执行效率。

总的来说,HotSpot虚拟机使用方法调用计数器和回边计数器作为基于计数器的热点探测方法,以识别热点代码并进行优化,从而提高Java应用程序的性能。

四、常见JIT优化手段

1. 公共子表达式消除

公共子表达式消除是JVM JIT编译器的一种优化技术,用于减少重复计算,提高程序的执行效率。

公共子表达式是指在一个程序中多次出现的计算表达式,通过公共子表达式消除优化,可以将重复的计算合并为一次计算,减少不必要的计算开销。

以下是一个简单的示例代码,展示了公共子表达式的消除优化:

public class CommonSubexpressionEliminationDemo {public static void main(String[] args) {int a = 5;int b = 3;int c = a * b + 2; // 公共子表达式 a * bint d = a * b + 2; // 公共子表达式 a * bSystem.out.println(c);System.out.println(d);}
}

在上述代码中,变量c和d都进行了相同的计算表达式 a * b + 2,通过公共子表达式消除优化,JVM JIT编译器会将重复的计算合并为一次计算。

总结:

公共子表达式消除可以减少重复计算,提高程序的执行效率。
JVM JIT编译器会通过识别重复的计算表达式,并将其优化为一次计算。
使用公共子表达式消除优化可以减少不必要的计算开销,特别在循环中使用同样的表达式时效果更为明显。

2. 方法内联

方法内联是JVM JIT编译器的一种优化技术,用于减少方法调用的开销,提高程序的执行效率。

方法内联是指将某个方法的代码直接插入到调用该方法的地方,而不是通过方法调用的方式执行。这样可以减少方法调用的开销,包括栈帧的创建和销毁、参数传递等操作。

以下是一个简单的示例代码,展示了方法内联的优化:

public class MethodInliningDemo {public static void main(String[] args) {int a = 5;int b = 3;int c = add(a, b); // 方法调用int d = a + b; // 方法内联System.out.println(c);System.out.println(d);}public static int add(int a, int b) {return a + b;}
}

在上述代码中,变量c通过方法调用的方式计算结果,而变量d直接将方法的代码内联到调用处进行计算。

总结:

方法内联可以减少方法调用的开销,提高程序的执行效率。
JVM JIT编译器会通过识别适合内联的方法,并将其优化为直接插入到调用处执行。
使用方法内联优化可以减少方法调用的开销,特别是在频繁调用的方法中效果更为明显。
需要注意的是,过多的方法内联可能会导致代码膨胀,增加编译时间和内存消耗。因此,在使用方法内联时需要权衡代码的大小和性能的提升。

3. 逃逸分析

逃逸分析是JVM JIT编译器的一种优化技术,用于分析对象的作用域,确定对象是否会逃逸出方法的范围,从而对对象的内存分配进行优化。

逃逸分析的目的是找出那些不会逃逸出方法的对象,将它们分配在栈上而不是堆上,以减少垃圾回收的开销。

以下是一个简单的示例代码,展示了逃逸分析的优化:

public class EscapeAnalysisDemo {public static void main(String[] args) {User user = createUser("Alice"); // 对象逃逸System.out.println(user.getName());}public static User createUser(String name) {return new User(name); // 对象逃逸}static class User {private String name;public User(String name) {this.name = name;}public String getName() {return name;}}
}

在上述代码中,createUser方法中创建的User对象会逃逸出方法的范围,即被外部引用所使用。

总结:

  • 逃逸分析是JVM JIT编译器的一种优化技术,用于分析对象的作用域,确定对象是否会逃逸出方法的范围。

  • 逃逸分析的目的是找出那些不会逃逸出方法的对象,将它们分配在栈上而不是堆上,以减少垃圾回收的开销。

  • 逃逸分析可以减少堆的分配和垃圾回收的开销,提高程序的执行效率。

  • 需要注意的是,逃逸分析并不是一项绝对有效的优化技术,它只能在特定的场景下才能生效,而且对于大部分应用程序来说,堆的分配和垃圾回收开销并不是性能瓶颈,因此逃逸分析的作用有限。

  • 逃逸分析在JVM中是通过参数来进行控制的。在JDK7及以后的版本中,默认情况下逃逸分析是开启的。

以下是一些与逃逸分析相关的JVM参数:

  • -XX:+DoEscapeAnalysis:启用逃逸分析。默认情况下是开启的。
  • -XX:-DoEscapeAnalysis:禁用逃逸分析。
  • -XX:+PrintEscapeAnalysis:打印逃逸分析的相关信息。
    -XX:+EliminateLocks:通过逃逸分析来消除不必要的锁。
  • 需要注意的是,逃逸分析的效果是与具体的JVM实现相关的,不同的JVM可能对逃逸分析的支持和优化程度有所不同。因此,对于一些特定的场景,可能需要根据实际情况进行适当的调整和优化。

3.1 逃逸分析之标量替换

标量替换是逃逸分析的一种优化技术,它将对象拆解成独立的标量(单个的基本类型或对象引用),并将这些标量分别分配在栈上或寄存器中,从而避免了对象的创建和访问操作。

下面是一个简单的示例代码,用于演示标量替换的效果:

public class ScalarReplacementDemo {public static void main(String[] args) {long startTime = System.currentTimeMillis();for (int i = 0; i < 10000000; i++) {Point point = new Point(i, i); // 创建一个Point对象int sum = point.x + point.y; // 使用Point对象的属性进行计算}long endTime = System.currentTimeMillis();System.out.println("Time taken: " + (endTime - startTime) + "ms");}static class Point {int x;int y;public Point(int x, int y) {this.x = x;this.y = y;}}
}

在上述代码中,我们循环创建了10000000个Point对象,并对每个对象的属性进行了相加操作。如果逃逸分析开启且标量替换生效,JVM会将Point对象的属性x和y分别替换为两个独立的局部变量,并将它们分配在栈上,从而避免了对Point对象的创建和访问。

总结:

  • 标量替换是逃逸分析的一种优化技术,将对象拆解成独立的标量并在栈上或寄存器中分配。
  • 标量替换能够避免对象的创建和访问操作,从而提高程序的性能。
  • 要启用标量替换,需要确保逃逸分析开启,并且JVM在运行时会自动进行标量替换的优化。
  • 在编写代码时,可以通过适当的代码设计来帮助JVM进行标量替换优化,例如使用不可变对象或局部变量等。

3.2 逃逸分析之栈上分配

栈上分配是逃逸分析的另一种优化技术,它将某些对象的内存分配在栈上而不是堆上。栈上分配可以减少对象在堆上的分配和回收的开销,提高程序的性能。

下面是一个简单的示例代码,用于演示栈上分配的效果:

public class StackAllocationDemo {public static void main(String[] args) {long startTime = System.currentTimeMillis();for (int i = 0; i < 10000000; i++) {Point point = createPoint(i, i); // 创建一个Point对象,并返回其引用int sum = point.x + point.y; // 使用Point对象的属性进行计算}long endTime = System.currentTimeMillis();System.out.println("Time taken: " + (endTime - startTime) + "ms");}static Point createPoint(int x, int y) {return new Point(x, y);}static class Point {int x;int y;public Point(int x, int y) {this.x = x;this.y = y;}}
}

在上述代码中,我们循环创建了10000000个Point对象,并对每个对象的属性进行了相加操作。如果逃逸分析开启且栈上分配生效,JVM会将Point对象的内存分配在栈上而不是堆上,从而减少了堆上对象的分配和回收的开销。

总结:

  • 栈上分配是逃逸分析的一种优化技术,将某些对象的内存分配在栈上而不是堆上。
  • 栈上分配能够减少对象在堆上的分配和回收的开销,提高程序的性能。
  • 要启用栈上分配,需要确保逃逸分析开启,并且JVM在运行时会自动进行栈上分配的优化。
  • 在编写代码时,可以通过适当的代码设计来帮助JVM进行栈上分配优化,例如将对象的作用域限制在方法内部、使用局部变量等。

3.3 逃逸分析之同步消除

同步消除是逃逸分析的另一种优化技术,它通过分析代码中的同步操作,判断是否可以消除这些同步操作从而提高程序的性能。

下面是一个简单的示例代码,用于演示同步消除的效果:

public class SynchronizationEliminationDemo {public static void main(String[] args) {long startTime = System.currentTimeMillis();for (int i = 0; i < 10000000; i++) {synchronizedMethod();}long endTime = System.currentTimeMillis();System.out.println("Time taken: " + (endTime - startTime) + "ms");}static void synchronizedMethod() {synchronized (SynchronizationEliminationDemo.class) {// 同步块中的代码}}
}

在上述代码中,我们循环调用了10000000次synchronizedMethod方法,该方法包含了一个同步块。如果逃逸分析开启且同步消除生效,JVM会判断到synchronizedMethod方法没有逃逸到其他线程中,因此可以消除同步操作,从而提高程序的性能。

总结:

  • 同步消除是逃逸分析的一种优化技术,通过分析代码中的同步操作,判断是否可以消除这些同步操作从而提高程序的性能。
  • 同步消除的前提是逃逸分析开启,并且JVM能够确定同步操作没有逃逸到其他线程中。
  • 同步消除可以减少线程间的同步开销,提高程序的并发性能。
    在编写代码时,可以通过避免不必要的同步操作、合理设计对象的作用域等方式帮助JVM进行同步消除优化。

五、JIT优化可能引发的问题

一旦我们理解了JIT编译的原理,就会明白JIT优化是在运行时进行的,并且并非在Java进程刚启动时就能立即进行优化的。它需要一定的执行时间来确定哪些代码是热点代码。

因此,在JIT优化开始之前,所有的请求都需要经过解释执行,这个过程相对较慢。尤其是在应用的请求量较大时,这个问题会更加明显。在应用启动过程中,大量的请求涌入会导致解释器持续努力工作。

如果解释器对CPU资源占用较高,就会间接导致CPU和负载等指标飙升,进而降低应用的性能。这也是为什么在应用发布过程中,刚刚重启好的应用会出现大量超时问题的原因。

随着请求不断增加,JIT优化会被触发,这使得后续的热点请求不再需要解释执行,而是直接运行JIT优化后缓存的机器码。

✨主要有两种解决思路:✨

1. 提升JIT优化的效率

一种方法是借鉴阿里研发的JDK Dragonwell,它相比于OpenJDK提供了一些专有特性,其中包括JwarmUp技术。该技术通过记录上一次Java应用运行时的编译信息到文件中,在下次应用启动时读取该文件,实现提前完成类加载、初始化和方法编译,跳过解释阶段,直接执行编译好的机器码。

2. 降低瞬时请求量

在应用刚启动时,通过调节负载均衡,逐渐增加流量,让应用在小流量下触发JIT优化,等优化完成后再逐渐增加流量。

这种方法类似于缓存预热的思想。在应用刚启动时,不要立即将大量流量分发给它,而是先分配一小部分流量,通过这部分流量触发JIT优化。等优化完成后,再逐渐增加流量。

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

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

相关文章

【android12-linux-5.1】【ST芯片】驱动与HAL移植后数据方向异常

ST的传感器驱动与HAL一直成功后&#xff0c;能拿到数据了&#xff0c;但是设备是横屏&#xff0c;系统默认是竖屏。就会出现屏幕自动转动时方向是错的的情况&#xff0c;设备横立展示的是竖屏&#xff0c;设备竖立展示的是横屏。 这个是PCB上设计的传感器贴片方向和横屏不一致…

windows10系统安装docker desktop超常见问题

问题报错&#xff1a; An unexpected error was encountered while executing a WSLcommand. Common causes include access rights issues, which occurafter waking the computer or not being connected to your domain/active directory. Please try shutting WSL down (w…

深度学习基础知识-pytorch数据基本操作

1.深度学习基础知识 1.1 数据操作 1.1.1 数据结构 机器学习和神经网络的主要数据结构&#xff0c;例如 0维&#xff1a;叫标量&#xff0c;代表一个类别&#xff0c;如1.0 1维&#xff1a;代表一个特征向量。如 [1.0&#xff0c;2,7&#xff0c;3.4] 2维&#xff1a;就是矩…

SAP 之如何定义功能范围Function Area

目录 目录 前言 一、注意点 二、使用步骤 1. Step by step 2. 其它功能 总结 前言 在SAP中&#xff0c;FA功能范围是一个组织单元&#xff0c;一般根据活动对产生的运营费用进行分类。例如生产、管理、销售、研发等&#xff0c;可以分配给成本中心Cctr、GL总账科目、Ord…

研磨设计模式day11观察者模式

目录 场景 代码示例 定义 观察者模式的优缺点 本质 何时选用 简单变型-区别对待观察者 场景 我是一家报社&#xff0c;每当我发布一个新的报纸时&#xff0c;所有订阅我家报社的读者都可以接收到 代码示例 报纸对象 package day11观察者模式;import java.util.Observ…

扫雷小游戏

目录 一.扫雷小游戏 二.游戏主体一览 ​编辑 三.模块化设计扫雷游戏 3.1打印欢迎菜单 3.2创建两个二维数组 3.3棋盘稍加修改 3.4布置雷 3.5排查雷 四.游戏总体代码 4.1game.h头文件 4.2game.c函数实现源文件 4.3游戏main函数主体 五.游戏效果图 一.扫雷小游戏 这是…

建设全球研发中心城市,长沙政协将会发挥怎样的作用?

政协力量&#xff0c;会如何推动一座城市的发展&#xff1f;在全国各大城市都力“拼经济、搞创新”的时代大背景下&#xff0c;人民政协的力量会如何助推长沙建设全球研发中心城市呢&#xff1f; 这是笔者此前一直很好奇的两个问题。8月24日&#xff0c;一场由长沙市政协举办的…

FFmpeg解码32k大分辨率出现如下错误:Picture size 32768x32768 is invalid

最近找到一张32k的jpeg图片&#xff0c;尝试用ffmpeg来进行解码&#xff0c;命令如下&#xff1a; ffmpeg -i enflame_32768-32768-420.jpg 32.yuv结果出现Picture size 32768x32768 is invalid的错误&#xff1a; 找到报错的代码文件imgutils.c&#xff0c;以及函数&#x…

字符串经典问题

1. 验证回文串 验证回文串 如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后&#xff0c;短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。 字母和数字都属于字母数字字符。 给你一个字符串 s&#xff0c;如果它是 回文串 &#xff0c;返回 t…

项目部署之后页面没有刷新怎么办?

最近项目部署成功之后&#xff0c;突然产品找我&#xff0c;上线之后&#xff0c;页面没有生效&#xff0c;这是怎么回事&#xff1f;我这是第一次部署这个项目,也不太清楚历史问题,接下来就慢慢寻找答案吧, 如果心急的可以直接看后面的总结&#xff0c;下面我们好好聊聊缓存的…

【RuoYi移动端】uniApp导入和引用uView2.0插件

一、打开uiew官网 安装 | uView 2.0 - 全面兼容 nvue 的 uni-app 生态框架 - uni-app UI 框架uView UI&#xff0c;是 uni-app 生态最优秀的 UI 框架&#xff0c;全面的组件和便捷的工具会让您信手拈来&#xff0c;如鱼得水https://uviewui.com/components/install.html 也可直…

基于Java+SpringBoot+Vue前后端分离在线考试与学习交流网页平台设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

SpringSession

Spring Session 是 Spring 的项目之一。Spring Session 提供了一套创建和管理 Servlet HttpSession 的方案&#xff0c;默认采用外置的 Redis 来存储 Session 数据&#xff0c;以此来解决 Session 共享的 问题。(springsession储存session数据的方式有很多&#xff0c;我们常…

excel中两列数据生成折线图

WPS中excel的两列数据&#xff0c;第一列为x轴&#xff0c;第二列为y轴&#xff0c;生成折线图&#xff0c;并生成拟合函数。 1.选中两列数据&#xff0c;右击选择插入图表&#xff0c;选择XY&#xff08;散点图&#xff09;&#xff0c;生成散点折线图 2.选中图中散点&#x…

SpringCloud 教程 | 第一篇: 服务的注册与发现(Eureka)

一、spring cloud简介 spring cloud 为开发人员提供了快速构建分布式系统的一些工具&#xff0c;包括配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等。它运行环境简单&#xff0c;可以在开发人员的电脑上跑。另外说明spring cloud是基…

Web服务器基础 http协议

文章目录 1.Web基础1.1MIME1.2 URI 和 URL1.2.1定义1.2.2两者的区别 2.静态资源和动态资源2.1 静态资源2.2 动态资源 3.HTTP协议3.1HTTP协议简介3.2HTTP协议的版本及区别3.2.1http协议版本3.2.2http1.0和1.1的区别 3.3HTTP请求报文3.4HTTP请求访问的过程1、建立连接&#xff1a…

服务器CPU飚高排查

排查思路 当正在运行的Java服务导致服务器的CPU突然飙高时&#xff0c;我们该如何排查定位到哪个接口的哪行代码导致CPU飙高的问题呢&#xff1f;我主要提供两个方案&#xff1a; jstackarthas 准备工作 代码准备 现在需要准备一段可以让服务器CPU飙高的代码以及把代码部署…

Go framework-Kratos

目录 一、Go framework 1、Kratos 介绍 1.1、Kratos 框架开发依赖安装 2、初始化 Kratos 项目 3、使用 Protobuf 、生成 Go 代码 3.1、增加proto文件模板 3.2、修改proto文件模板 3.3、根据修改完的模板文件生成客户端代码 3.4、根据修改完的模板文件生成服务端代码 …

Windows平台Unity下播放RTSP或RTMP如何开启硬解码?

我们在做Windows平台Unity播放RTMP或RTSP的时候&#xff0c;遇到这样的问题&#xff0c;比如展会、安防监控等场景下&#xff0c;需要同时播放多路RTMP或RTSP流&#xff0c;这样对设备性能&#xff0c;提出来更高的要求。 虽然我们软解码&#xff0c;已经做的资源占有非常低了…

在外SSH远程连接macOS服务器

文章目录 前言1. macOS打开远程登录2. 局域网内测试ssh远程3. 公网ssh远程连接macOS3.1 macOS安装配置cpolar3.2 获取ssh隧道公网地址3.3 测试公网ssh远程连接macOS 4. 配置公网固定TCP地址4.1 保留一个固定TCP端口地址4.2 配置固定TCP端口地址 5. 使用固定TCP端口地址ssh远程 …