JVM C1,C2编译器以及分层编译

当JVM的初始化完成后,类在调用执行过程中,执行引擎会把字节码转换成机器码,然后在操作系统中才能执行。在字节码转换为机器码的过程中,虚拟机中还存在着一道编译,那就是即时编译JIT。

最初,JVM中的字节码是由解释器(Interpreter)完成编译的,当虚拟机发现某个方法或代码块运行特别频繁时,就会把这些代码认定为热点代码。

为了提高热点代码的执行效率,在运行时,即时编译器(JIT, Just In Time)会把这些代码编译成与本地平台相关的机器码,并进行各层次的优化,然后保存到内存中。

怎么样才会被认为是热点代码呢?JVM中会设置一个阈值,当方法或者代码块的在一定时间内的调用次数超过这个阈值时就会被编译,存入codeCache中。当下次执行时,再遇到这段代码,就会从codeCache中读取机器码,直接执行,以此来提升程序运行的性能。整体的执行过程大致如下图所示:

C1编译器

C1编译器是一个简单快速的编译器,主要的关注点在于局部性的优化,适用于执行时间较短或者对启动性能有要求的程序,也称Client Compiler,例如,GUI应用对界面启动速度就有一定要求。

C2编译器

C2编译器是为长期运行的服务器端应用程序做性能调优的编译器,适用于执行时间较长或者对峰值性能有要求的程序,也称为Server Compiler,例如,服务器上长期运行的Java应用对稳定运行就有一定的要求。

Graal Compiler

从JDK 9开始,Hotspot VM中集成了一种新的Server Compiler,Graal编译器。JVM会在解释执行的时候收集程序运行的各种信息,然后编译器会根据这些信息进行一些基于预测的激进优化,比如分支预测,根据程序不同分支的运行概率,选择性地编译一些概率较大的分支。Graal比C2更加青睐这种优化,所以Graal的峰值性能通常要比C2更好。

Graal编译器可以通过Java虚拟机参数-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler启用。当启用时,它将替换掉HotSpot中的C2编译器,并响应原本由C2负责的编译请求。

分层编译

在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编译,也是将字节码编译为本地代码,但是会启用一些编译耗时较长的优化,甚至会根据性能监控信息进行一些不可靠的激进优化。

profiling就是收集能够反映程序执行状态的数据。其中最基本的统计数据就是方法的调用次数,以及循环回边的执行次数。

通常情况下,C2代码的执行效率要比C1代码的高出30%以上。C1层执行的代码,按执行效率排序从高至低则是1层>2层>3层。这5个层次中,1层和4层都是终止状态,当一个方法到达终止状态后,只要编译后的代码并没有失效,那么JVM就不会再次发出该方法的编译请求的。服务实际运行时,JVM会根据服务运行情况,从解释执行开始,选择不同的编译路径,直到到达终止状态。

相关参数

-Xint:相当于-XX:-UseCompiler,以解释器Interpreter模式运行(关闭C1, C2)

-XX:TieredStopAtLevel=4:使用C1,C2编译器,并停留在第四层,这也是默认值

-XX:+TieredCompilation:开启分层编译,JDK8之后默认开启

-XX:-TieredCompilation:只开启 C2,关闭分层编译

-XX:-OmitStackTraceInFastThrow:关闭fast throw优化

-XX:InitialCodeCacheSize:codeCache初始大小

-XX:ReservedCodeCacheSize:codeCache最大大小,默认240MB

-XX:CompileThreshold:当方法的调用次数和循环回边的次数的和,超过由参数-XX:CompileThreshold指定的阈值时(使用C1时,默认值为1500;使用C2时,默认值为10000),就会触发即时编译

开启分层编译的情况下,-XX:CompileThreshold参数设置的阈值将会失效,触发编译会由以下的条件来判断:

  • 方法调用次数大于由参数-XX:TierXInvocationThreshold指定的阈值乘以系数。
  • 方法调用次数大于由参数-XX:TierXMINInvocationThreshold指定的阈值乘以系数,并且方法调用次数和循环回边次数之和大于由参数-XX:TierXCompileThreshold指定的阈值乘以系数时。

默认C1, C2编译器线程数由CPU核心数决定。

 -XX:CICompilerCount=N: N/3是C1编译线程数,(N - N/3)是C2编译线程数,比如C1CompileCount=6,那么c1线程有2个,c2线程有4个。

-XX:+PrintCompilation: 输出应用代码编译细节,方便调优

JVM查看参数

-XX:+PrintFlagsInitial: 这个参数显示在处理参数之前所有可设置的参数及它们的值,然后直接退出程序

-XX:+PrintCommandLineFlags: 这个参数的作用是显示出VM初始化完毕后所有跟最初的默认值不同的参数及它们的值

-XX:+PrintFlagsFinal: 显示JVM初始化完后的参数值

通过java -XX:+PrintFlagsFinal -version查看默认信息。

参考:

OmitStackTraceInFastThrow与JVM的C1,C2编译器 - 掘金

基本功 | Java即时编译器原理解析及实践 - 美团技术团队

JVM c1, c2 compiler thread – High CPU consumption?

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 我可以把编译器分为两类:C1编译器C2编译器C1编译器是一种高级的编译器,它能够将源代码编译为机器语言。它通常用于编译大型程序。C2编译器是一种轻量级的编译器,它可以将源代码编译为可执行文件。它通常用于编译小型程序。 ### 回答2: C1编译器C2编译器是两个常见的C语言编译器,它们在一些方面有所不同。 首先,C1编译器是一种基于传统经典的编译技术的编译器。它通常采用单遍扫描的方式进行编译,并且在编译过程中生成目标代码。C1编译器的优点在于编译速度较快,适用于一些较为简单的项目和资源有限的环境。然而,由于其编译过程相对简单,生成的目标代码质量可能相对较低,可能存在一些性能上的损失。 而C2编译器则是更现代化和高级的编译器。它采用多遍扫描的方式进行编译,并且在编译过程中进行一系列的优化。C2编译器能够更好地分析代码,对程序进行静态检查,并生成更高效的目标代码。因此,C2编译器的优点在于生成的目标代码质量较高,程序性能通常更好。但是,由于其编译过程相对复杂,所以编译速度可能较慢。 此外,C2编译器通常支持更多的C语言标准和扩展,以及更多的优化选项。它还可能支持更多的目标平台,如不同的处理器架构和操作系统。C2编译器通常具有更丰富的功能和更好的兼容性,适合用于开发大型复杂项目和追求最佳性能的应用。 综上所述,C1编译器C2编译器编译原理和性能上有所不同。选择使用哪个编译器取决于项目的要求和限制,以及开发者对性能和功能的需求。 ### 回答3: C1编译器C2编译器是两个不同的编译器,它们在编译过程中有一些区别。 首先,C1编译器是Sun Microsystems的一个开发项目,它是Java虚拟机(JVM)的一个部分。C1编译器主要负责在运行时将字节码(Java源代码的中间表示形式)转换为本地机器代码。C1编译器的目标是快速编译,以便在短时间内生成本地机器代码,但生成的代码质量相对较低。 而C2编译器也是Java虚拟机的一部分,它是JIT(即时编译器)的一部分。C2编译器通过将热门的字节码转换成高效的本地机器代码来优化Java应用程序的性能。C2编译器采用了更高级的优化技术,包括代码内联、死代码消除、循环优化等,以生成更高质量的本地代码。C2编译器编译过程中需要更长的时间来进行优化,并且会占用更多的内存。 总之,C1编译器C2编译器Java虚拟机中两个不同的编译器C1编译器主要用于快速编译生成本地代码,但生成的代码质量较低;而C2编译器则通过更高级的优化技术生成更高质量的本地代码,但编译过程需要更长时间和更多内存。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值