jvm中的jit即时编译及优化技术(代码片段)

cnndevelop cnndevelop     2023-04-28     515

关键词:

 

JVM Client 模式和 Server模式的区别

 
技术图片
查看JVM模式

通过 java -version 可查看 JVM 所处的模式,并可以通过修改配置文件进行配置,那它们有什么区别呢?

Server:-Server 模式启动时,速度较慢,但是启动之后,性能更高,适合运行服务器后台程序

Client:-Client 模式启动时,速度较快,启动之后不如 Server,适合用于桌面等有界面的程序

热点代码

理解

当虚拟机发现某个方法或代码块的运行特别频繁时,就会把这些代码认定为“热点代码”。

热点代码的分类

  • 被多次调用的方法

一个方法被调用得多了,方法体内代码执行的次数自然就多,成为“热点代码”是理所当然的。

  • 被多次执行的循环体

一个方法只被调用过一次或少量的几次,但是方法体内部存在循环次数较多的循环体,这样循环体的代码也被重复执行多次,因此这些代码也应该认为是“热点代码”。

上面提到的多次是一个不具体的词语,那到底是多少次才能成为热点代码呢?

如何检测热点代码

判断一段代码是否是热点代码,是否需要触发即使编译,这样的行为称为热点探测,热点探测并不一定知道方法具体被调用了多少次,目前主要的热点探测判定方式有两种:

  • 基于采样的热点探测:采用这种方法的虚拟机会周期性地检查各个线程的栈顶如果发现某个(或某些)方法经常出现在栈顶,那这个方法就是“热点方法”

优点:实现简单高效,容易获取方法调用关系(将调用堆栈展开即可)

缺点:不精确,容易因为因为受到线程阻塞或别的外界因素的影响而扰乱热点探测

  • 基于计数器的热点探测:采用这种方法的虚拟机会为每个方法(甚至是代码块)建立计数器,统计方法的执行次数,如果次数超过一定的阈值就认为它是“热点方法”

优点:统计结果精确严谨

缺点:实现麻烦,需要为每个方法建立并维护计数器,不能直接获取到方法的调用关系

HotSpot使用第二种 - 基于计数器的热点探测方法。

确定了检测热点代码的方式,如何计算具体的次数呢?

计数器的种类(两种共同协作)

  • 方法调用计数器:这个计数器用于统计方法被调用的次数。默认阈值在 Client 模式下是 1500 次,在 Server 模式下是 10000 次

  • 回边计数器:统计一个方法中循环体代码执行的次数

了解了热点代码和计数器有什么用呢?达到计数器的阈值会触发后文讲解的即时编译,也就是说即时编译是需要达到某种条件才会触发的,先写结论,后文讲解什么是即时编译器。

两个计数器的协作(这里讨论的是方法调用计数器的情况):当一个方法被调用时,会先检查该方法是否存在被 JIT(后文讲解) 编译过的版本,如果存在,则优先使用编译后的本地代码来执行。如果不存在已被编译过的版本,则将此方法的调用计数器加 1,然后判断方法调用计数器与回边计数器之和是否超过方法调用计数器的阈值。如果已经超过阈值,那么将会向即时编译器提交一个该方法的代码编译请求。

当编译工作完成之后,这个方法的调用入口地址就会被系统自动改成新的,下一次调用该方法时就会使用已编译的版本。

什么是字节码、机器码、本地代码?

字节码是指平常所了解的 .class 文件,Java 代码通过 javac 命令编译成字节码

机器码和本地代码都是指机器可以直接识别运行的代码,也就是机器指令

字节码是不能直接运行的,需要经过 JVM 解释或编译成机器码才能运行

此时你要问了,为什么 Java 不直接编译成机器码,这样不是更快吗?

1. 机器码是与平台相关的,也就是操作系统相关,不同操作系统能识别的机器码不同,如果编译成机器码那岂不是和 C、C++差不多了,不能跨平台,Java 就没有那响亮的口号 “一次编译,到处运行”;

2.之所以不一次性全部编译,是因为有一些代码只运行一次,没必要编译,直接解释运行就可以。而那些“热点”代码,反复解释执行肯定很慢,JVM在运行程序的过程中不断优化,用JIT编译器编译那些热点代码,让他们不用每次都逐句解释执行;

3.还有一方面的原因是后文讲解的解释器与编译器共存的原因。

什么是 JIT ?

为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器称为即时编译器(Just In Time Compiler),简称 JIT 编译器

什么是编译和解释?

编译器:把源程序的每一条语句都编译成机器语言,并保存成二进制文件,这样运行时计算机可以直接以机器语言来运行此程序,速度很快;

解释器:只在执行程序时,才一条一条的解释成机器语言给计算机来执行,所以运行速度是不如编译后的程序运行的快的;

通过javac命令将 Java 程序的源代码编译成 Java 字节码,即我们常说的 class 文件。这是我们通常意义上理解的编译。

字节码并不是机器语言,要想让机器能够执行,还需要把字节码翻译成机器指令。这个过程是Java 虚拟机做的,这个过程也叫编译。是更深层次的编译。(实际上就是解释,引入 JIT 之后也存在编译)

此时又有疑惑了,Java不是解释执行的吗?

没错,Java 需要将字节码逐条翻译成对应的机器指令并且执行,这就是传统的 JVM 的解释器的功能,正是由于解释器逐条翻译并执行这个过程的效率低,引入了 JIT 即时编译技术。

必须指出的是,不管是解释执行,还是编译执行,最终执行的代码单元都是可直接在真实机器上运行的机器码,或称为本地代码

附一张图来理解

 
技术图片
image

编译原理参考:深入分析Java的编译原理

为何 HotSpot 虚拟机要使用解释器与编译器并存的架构?

解释器与编译器两者各有优势

解释器:当程序需要迅速启动和执行的时候,解释器可以首先发挥作用,省去编译的时间,立即执行。

编译器:在程序运行后,随着时间的推移,编译器逐渐发挥作用,把越来越多的代码编译成本地代码之后,可以获取更高的执行效率。

两者的协作:在程序运行环境中内存资源限制较大时,可以使用解释执行节约内存,反之可以使用编译执行来提升效率。当通过编译器优化时,发现并没有起到优化作用,,可以通过逆优化退回到解释状态继续执行。

即时编译器与 Java 虚拟机的关系

即时编译器并不是虚拟机必需的部分,Java 虚拟机规范并没有规定 Java 虚拟机内必须要有即时编译器的存在,更没有限定或指导即时编译器应该如何去实现。

但是,即时编译器编译性能的好坏、代码优化程度的高低却是衡量一款商用虚拟机优秀与否的最关键的指标之一。它也是虚拟机中最核心且最能体现虚拟机技术水平的部分。

即时编译器的分类

  • Client Compiler - C1编译器
  • Server Compiler - C2编译器

目前主流的 HotSpot 虚拟机(JDK1.7 及之前版本的虚拟机)默认采用一个解释器和其中一个编译器直接配合的方式工作,程序使用哪个编译器,取决于虚拟机运行的模式,就是文章开头提到的两种模式。

在 HotSpot 中,解释器和 JIT 即时编译器是同时存在的,他们是 JVM 的两个组件。对于不同类型的应用程序,用户可以根据自身的特点和需求,灵活选择是基于解释器运行还是基于 JIT 编译器运行。HotSpot 为用户提供了几种运行模式供选择,可通过参数设定,分别为:解释模式、编译模式、混合模式,HotSpot 默认是混合模式,需要注意的是编译模式并不是完全通过 JIT 进行编译,只是优先采用编译方式执行程序,但是解释器仍然要在编译无法进行的情况下介入执行过程。

分层编译

产生的原因:由于即时编译器编译本地代码需要占用程序运行时间,要编译出优化程度更高的代码,所花费的时间可能更长;而且要想编译出优化程度更高的代码,解释器可能还要替编译器收集性能监控信息,这对解释执行的速度也有影响。为了在程序启动响应速度与运行效率之间达到最佳平衡,HotSpot 虚拟机启用分层编译的策略

分层编译根据编译器编译、优化的规模与耗时,划分出不同的编译层次:

  • 第 0 层:程序解释执行,解释器不开启性能监控功能,可触发第 1 层编译。

  • 第 1 层:也称为 C1 编译,将字节码编译为本地代码,进行简单,可靠的优化,如有必要将加入性能监控的逻辑。

  • 第 2 层(或 2 层以上):也称为 C2 编译,也是将字节码编译为本地代码,但是会启用一些编译耗时较长的优化,甚至会根据性能监控信息进行一些不可靠的激进优化。

实施分层编译后,Client Compiler 和 Server Compiler 将会同时工作,许多代码都可能会被多次编译看,用 Client Compiler 获取更高的编译速度,用 Server Compiler 获取更好的编译质量,在解释执行的时候也无须再承担收集性能监控信息的任务。

编译优化技术

Java 程序员有一个共识,以编译方式执行本地代码比解释执行方式更快,之所以有这样的共识,除去虚拟机解释执行字节码时额外消耗时间的原因外,还有一个重要的原因就是虚拟机设计团队几乎把对代码的所有优化措施都集中在了即时编译器中,因此一般来说,即时编译器产生的本地代码会比 javac 产生的字节码更优秀。以下是具有代表性的 HotSpot 虚拟机的即时编译器在生成代码时采用的代码优化技术:

  • 语言无关的经典优化技术之一:公共子表达式消除

如果一个表达式 E 已经计算过了,并且从先前的计算到现在 E 中所有变量的值都没有发生变化,那么 E 的这次出现就成为了公共子表达式。对于这种表达式,没必要花时间再对它进行计算,只需要直接使用前面计算过的表达式结果代替 E 就可以了。

例子:int d = (c*b) * 12 + a + (a+ b * c) -> int d = E * 12 + a + (a+ E)

  • 语言相关的经典优化技术之一:数组范围检查消除

在 Java 语言中访问数组元素的时候系统将会自动进行上下界的范围检查,超出边界会抛出异常。对于虚拟机的执行子系统来说,每次数组元素的读写都带有一次隐含的条件判定操作,对于拥有大量数组访问的程序代码,这无疑是一种性能负担。Java 在编译期根据数据流分析可以判定范围进而消除上下界检查,节省多次的条件判断操作。

  • 最重要的优化技术之一:方法内联

简单的理解为把目标方法的代码“复制”到发起调用的方法中,消除一些无用的代码。只是实际的 JVM 中的内联过程很复杂,在此不分析。

  • 最前沿的优化技术之一:逃逸分析

逃逸分析的基本行为就是分析对象动态作用域:当一个对象在方法中杯定义后,它可能被外部方法所引用,例如作为调用参数传递到其他方法中,称为方法逃逸。甚至可能被外部线程访问到,譬如赋值给类变量或可以在其他线程中访问的实例变量,称为线程逃逸。

如果能证明一个对象不会逃逸到方法或线程之外,也就是别的方法或线程无法通过任何途径访问到这个对象,则可以为这个变量进行一些高效的优化:

  • 栈上分配:将不会逃逸的局部对象分配到栈上,那对象就会随着方法的结束而自动销毁,减少垃圾收集系统的压力。
  • 同步消除:如果该变量不会发生线程逃逸,也就是无法被其他线程访问,那么对这个变量的读写就不存在竞争,可以将同步措施消除掉(同步是需要付出代价的)
  • 标量替换:标量是指无法在分解的数据类型,比如原始数据类型以及reference类型。而聚合量就是可继续分解的,比如 Java 中的对象。标量替换如果一个对象不会被外部访问,并且对象可以被拆散的话,真正执行时可能不创建这个对象,而是直接创建它的若干个被这个方法使用到的成员变量来代替。这种方式不仅可以让对象的成员变量在栈上分配和读写,还可以为后后续进一步的优化手段创建条件。


作者:云大数据社区
链接:https://www.jianshu.com/p/fbced5b34eff
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

jvm--运行期优化;jit(代码片段)

一、即时编译(JIT)JIT:JustInTimeCompiler,即时编译器这是针对解释型语言而言的,而且并非虚拟机必须,是一种优化手段。Hotspot就有这种技术,Java虚拟机标准对JIT的存在没有作出任何规范,这是虚... 查看详情

jvm虚拟机aot和jit即时编译(代码片段)

文章目录JVM虚拟机AOT和JIT即时编译1、AOT2、JITJVM虚拟机AOT和JIT即时编译1、AOTAOT是AheadOfTime,运行前编译与JIT编译对比JIT吞吐量高,有运行时性能加成,可以跑得更快,并可以做到动态生成代码等,但是相对启动... 查看详情

双管齐下,jvm内部优化与jvm性能调优(代码片段)

...2.2.3条件编译三、运行时优化(核心:JIT编译器/即时编译器)3.1HotSpot虚拟机内的JIT编译器3.1.1编译器和解释器并存的架构3.1.2编译对象和触发条件3.1.3不同的编译过程3.2编译优化技术3.2.1公 查看详情

转:什么是即时编译(jit)!?openjdkhotspotvm剖析

重点应用程序可以选择一个适当的即时编译器来进行接近机器级的性能优化。分层编译由五层编译构成。分层编译提供了极好的启动性能,并指导编译的下一层编译器提供高性能优化。提供即时编译相关诊断信息的JVM开关。像内... 查看详情

浅析jit即时编译技术

即时编译回顾HotSpot虚拟机执行Java程序时,先通过解释器对代码解释执行,发现某个方法或代码块执行比较频繁后,对热点代码进行编译,编译后生成与本地平台相关的机器码,再去执行机器码获得较高的运行效率。必要时,也... 查看详情

jvm中的jit

 JVM中的JIT     介绍Java虚拟机的文章或者书籍总会提到Java虚拟机中的JIT编译器,可是JIT编译器到底是什么?为什么需要JIT编译呢?JIT编译器,是JustInTime编译的意思,又称即时编译。 Java程序是先从源代... 查看详情

jit即时编译

...oogle+用电子邮件发送本页面JIT是justintime的缩写,也就是即时编译编译器。使用即时编译器技术,能够加速Java程序的执行速度。下面,就对该编译器技术做个简单的讲解。首先,我们大家都知道,通常通过javac将... 查看详情

java虚拟机即时编译器技术jit

...术用以提升速度,尤其是与加载器操作相关的,被称为“即时”(Just-In-Time,JIT)编译器的技术。这种技术可以把程序全部或部分翻译成本地机器码(这本来是JVM的工作),程序运行速度因此得以提升。当需要装载某个类时,编译器... 查看详情

jit

...达式消除2.方法内联:JIT会将被调用的方法直接用方法体中的代码代替,减少了方法调用过程中的压栈出栈。3.jdk7后默认开启逃逸分析,逃逸是指当一个对象在方法中被定义,而它能被外部方法引用。  逃逸分析包括:全... 查看详情

jvm之方法区(代码片段)

...ss/method/field等元数据对象:字节码加载之后,JVM会根据其中的内容,为这个类生成Class/Method/Field等对象,它们用于描述一个类,通常在反射中用的比较多。不同于存储在堆中的java实例对象,这两种对象存储在方法区中。(3)stat... 查看详情

jit即时编译(史上最全)(代码片段)

...后面,水不知道有多深。首先,考察了大家对JVM即时编译组件的 查看详情

jvm系列之:jit中的virtualcall接口(代码片段)

...解了VirtualCall的定义并举例分析了VirtualCall在父类和子类中的优化。JIT对类可以进行优化,那么对于interface可不可以做同样的优化么?一起来看看吧。最常用的接口ListList应该是大家最最常用的接口了,我想这个大家应该不会反驳... 查看详情

day798.java编译-java性能调优实战(代码片段)

JVM即时编译器JIT,优化Java编译Hi,我是阿昌,今天学习记录的是关于JVM即时编译器JIT,优化Java编译。编译,一定会想到.java文件被编译成.class文件的过程,这个编译一般称为前端编译。Java的编译和运行过... 查看详情

jvm理论:(四/1)编译过程——早期(编译期)(代码片段)

...化措施。虚拟机设计团队把对性能的优化集中到了后端的即时编译器中,这样可以让那 查看详情

jvm理论:(四/2)编译过程——晚期(运行期)

...码,并进行各种层次的优化,完成这个任务的编译器称为即时编译器(JIT编译器)。  即时编译器并不是虚拟机必需的部分,但是,即时编译器编译性能的好坏、代码优化程度的高低却是衡量一款商用虚拟机优秀与否的最关键... 查看详情

javajava即时编译(jit)器原理解析及实践

1.概述转载:Java即时编译(JIT)器原理解析及实践一、导读常见的编译型语言如C++,通常会把代码直接编译成CPU所能理解的机器码来运行。而Java为了实现“一次编译,处处运行”的特性,把编译的过... 查看详情

jvm技术专题深入学习jit编译器实现机制「原理篇」(代码片段)

前提概要解释器Java程序最初是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁的时候,就会把这些代码认定为“热点代码”(hotspotcode)。正因为如此,我们的hotspot的虚... 查看详情

小师妹学jvm之:jit中的printassembly(代码片段)

...bly。使用PrintAssembly小师妹:F师兄,上次你给我介绍了java中的字节码,还有JIT中的Log 查看详情