即时编译(Just-In-Time Compilation, JIT)是一种强大的技术,旨在增强基于字节码的语言(如Java、.NET)的运行时性能。它的工作原理是在程序运行过程中动态地将频繁执行的字节码转换成本地机器码,从而大幅提高执行效率。这一过程克服了纯解释执行的性能瓶颈,同时保留了跨平台的灵活性。
HotSpot是Java虚拟机的一个著名实现,它通过三种主要的即时编译器来平衡启动时间和运行时性能:
-server -XX:+TieredCompilation
参数开启。
逃逸分析是JIT编译器中的一个高级特性,它分析对象的生命周期和作用域,判断对象是否“逃逸”出其创建的方法或线程,以此来决定是否可以采取进一步的优化措施。逃逸的两种情况包括:
当分析得知对象未发生逃逸时,可以执行以下优化:
-XX:+EliminateAllocations
可以开启标量替换,-XX:+PrintEliminateAllocations
查看。分层编译和逃逸分析在1.8中是默认是开启的
在即时编译(JIT)的背景下,编译阈值是一个关键参数,它决定了代码从解释执行过渡到编译执行的时机。这一机制确保了程序在频繁执行的“热点”代码上投资编译资源,以最大化性能提升,同时避免了对不常执行代码的过度优化,减少启动时间和内存占用。
-XX:CompileThreshold=<value>
,用户可以根据具体应用的需求调整这个阈值。减小该值可以让编译发生得更早,有助于快速提升性能,但可能会增加启动时间和内存使用;增大则相反,适用于那些启动时间敏感但运行时间较长的应用。
-XX:OnStackReplacePercentage=<percentage>
,这个参数指定了执行OSR编译的循环回边计数占标准编译阈值的比例。例如,若设置为93%,则当一个循环执行次数达到标准编译阈值的93%时,将会触发OSR编译。
-XX:CompileThreshold = 10000
-XX:OnStackReplacePercentage = 140
-XX:InterpreterProfilePercentage = 33
OSR trigger = (CompileThreshold * (OnStackReplacePercentage -
InterpreterProfilePercentage)) / 100 = 10700
其中trigger即为OSR编译的阈值。 那么如果把CompileThreshold设置适当小一点,是不是可以提早触发编译行为,减少在堆上生成User 对象?我们可以进行通过不同参数验证一下:
在我的机器中,当设置到1500时,在堆上生成的User对象反而升到4w个,目前还不清楚原因是啥… JIT编译在默认情况是异步进行的,当触发某方法或某代码块的优化时,先将其放入编译队列,然后由编 译线程进行编译,编译之后的代码放在CodeCache中,CodeCache的大小也是有限的,通过 -XX:- BackgroundCompilation 参数可以关闭异步编译,我们可以通过执行 java -cp . -Xmx3G -Xmn2G - server -XX:CompileThreshold=1 -XX:-TieredCompilation -XX:-BackgroundCompilation JVM 命 令看看同步编译的效果:在java堆上只生成了2个对象。
1、热点代码的编译过程是有成本的,如果逻辑复杂,编程成本更高; 2、编译后的代码会被存放在有大小限制的CodeCache中,如果CompileThreshold设置的太低,JIT会 将一大堆执行不那么频繁的代码进行编译,并放入CodeCache,导致之后真正执行频繁的代码没有足够 的空间存放;