JIT分析

本文详细介绍了Java的JIT(Just In Time)编译器,包括其工作原理、编译器与解释器的区别、分层编译的策略,以及C1和C2编译器的优化技术,如方法内联、去虚拟化、冗余消除等。JIT编译器通过动态编译和优化,提高了Java程序的运行效率,同时对比了Java与C/C++的编译器特点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JIT(Just In Time)

1.什么是Just In Time编译器?

在主流商用JVM(HotSpot、J9)中,Java程序一开始是通过解释器(Interpreter)进行解释执行的。当JVM发现某个方法或代码块运行特别频繁时,就会把这些代码认定为“热点代码(Hot Spot Code)”,然后JVM会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器称为:即时编译器(Just In Time Compiler,JIT)

JIT编译器是“动态编译器”的一种,相对的“静态编译器”则是指的比如:C/C++的编译器

JIT并不是JVM的必须部分,JVM规范并没有规定JIT必须存在,更没有限定和指导JIT。但是,JIT性能的好坏、代码优化程度的高低却是衡量一款JVM是否优秀的最关键指标之一,也是虚拟机中最核心且最能体现虚拟机技术水平的部分。

Hot Spot 编译 当 JVM 执行代码时,它并不立即开始编译代码。这主要有两个原因:

第一个原因,如果这段代码本身在将来只会被执行一次,那么从本质上看,编译就是在浪费精力。因为将代码翻译成 java 字节码相对于编译这段代码并执行代码来说,要快很多。

第二个原因是最优化,当 JVM 执行某一方法或遍历循环的次数越多,就会更加了解代码结构,那么 JVM 在编译代码的时候就做出相应的优化。

当然,如果一段代码频繁的调用方法,或是一个循环,也就是这段代码被多次执行,那么编译就非常值得了。因此,编译器具有的这种权衡能力会首先执行解释后的代 码,然后再去分辨哪些方法会被频繁调用来保证其本身的编译。Hot Spot VM 采用了 JIT compile 技术,将运行频率很高的字节码直接编译为机器指令执行以提高性能,所以当字节码被 JIT 编译为机器码的时候,要说它是编译执行的也可以。也就是说,运行时,部分代码可能由 JIT 翻译为目标机器指令(机器指令以 method 为翻译单位,还会保存起来,第二次执行就不用翻译了)直接执行。

我 们将在后面讲解编译的优化策略,这里,先举一个简单的例子:我们知道 equals() 这个方法存在于每一个 Java Object 中(因为是从 Object class 继承而来)而且经常被覆写。当解释器遇到 b = obj1.equals(obj2) 这样一句代码,它则会查询 obj1 的类型从而得知到底运行哪一个 equals() 方法。而这个动态查询的过程从某种程度上说是很耗时的。这时如果我们对这段代码进行编译优化,将equals直接编译向指向真正执行方法存在的某一块内存,运行起来会快很多。

2.编译器与解释器

首先,不是所有JVM都采用编译器和解释器并存的架构,但主流商用虚拟机,都同时包含这两部分。

2.1 配合过程

  1. 当程序需要迅速启动然后执行的时候,解释器可以首先发挥作用,编译器不运行从而省去编译时间,立即执行程序
  2. 在程序运行后,随着时间的推移,编译器逐渐发挥作用,把越来越多的代码编译成本地代码之后,可以获得更高的执行效率
  3. 当程序运行环境中内存资源限制较大(如部分嵌入式系统中),可以使用解释执行来节约内存;反之,则可以使用编译执行来提升效率。
  4. 同时,解释器还可以作为编译器(C2才会激进优化)激进优化(可以认为是优化的激进策略)时的一个“逃生门”(逃生门可以理解为犯错之后的回退),让编译器根据概率选择一些大多数时候都能提升运行速度的优化手段,当激进优化假设不成立。如:加载了新类后,类型继承结构出现变化,出现“罕见陷阱(Uncommon Trap)”时,可以通过逆优化(Deoptimization)退回到解释状态继续执行 (部分没有解释器的虚拟机,也会采用不进行激进优化的C1编译器担任“逃生门”的角色)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值