Java-即时编译(Just in time)

使用即时编译器 (just in time compiler)

编译是在程序执行时发生的,即时
这种编译仍然受制于平台。例如,JDK 8无法为Intel的Skylake处理器的最新指令集生成代码

热点编译

典型的应用程序中,只有一小部分代码会频繁执行,应用程序的性能取决于这些代码执行的快慢
这些关键的代码被称为程序的热点

当jvm执行代码的时候,并不是立即开始编译代码。

  1. 第一个原因判断代码执行频率
    1.如果代码执行一次,编译时浪费时间,这个时候使用解释器执行会更快:怎么指导只执行一次
    2.如果代码被重复执行多次,编译后的代码执行的更快。
    编译器权衡首先执行解释代码的原因之一
  2. 第二原因 优化
    1. jvm执行越多代码就越了解代码,更具信息可以做出编译优化
      equals方法的执行优化
      当解释器执行equals必须类类型,得知执行哪个equals方法
      这个时候jvm注意到每次执行equals都是java.lang.String类型,那么在编译的时候就直接使用String.equals方法,速度提升
      如果这是时候不适string.equals方法,这就涉及到逆优化和重新优化,总体是提升了速度,这种方式只有在代码运行一段时间才能进行

分层编译

常用代码编译标志

1.优化代码缓存
当jvm编译代码的时候,会在代码缓存中保存一系列汇编指令,代码缓存固定大小,一旦填满便不再进行编译
-XX:ReservedCodeCacheSize=N
初始大小2496KB——-XX:InitialCodeCacheSize=N
最大值是240M

2.检查编译过程
-XX:+PrintCompilation默认关闭状态,能够看到编译的情况
timestamp compilation_id attributes (tiered_level) method_name size deopt
栈上替换 On-Stack Relacement
当jvm认为某个方法需要编译,则放入队列然后继续执行,异步过程
如果下次执行则执行编译版本,如果已经编译完成
jvm必须有能力在循环还在运行的时候就能执行循环的编译版本,如果等待循环完成或者循环中的方法完成,这将是非常耗时的。
因此当循环代码完成编译后,jvm会替换在栈上执行的代码,循环的下一次迭代会执行速度更快的编译后的代码。
jstat -printcompilation pid 1000
每隔一秒输出进程的编译信息

高级编译标志

编译阈值

-XX:CompileThreshold 默认 10000
    禁用分层编译时调整该参数    

编译线程

当方法可以被编译,会放到编译队列中。这个队列由一个或多个后台线程处理。
调用次数多的方法具有更高的优先级进行编译,优先级队列。(编译顺序乱序的原因是因为优先级导致)

内联

-XX:-Inline 默认开启,无法查看jvm如何内联的代码,必须通过编译器输出结果才能确认

逃逸分析

-XX:+DeEscapeAnalysis

CPU 相关代码

即时编译器的一个优点是可以根据处理器信息生产代码
是否能满足处理器的指令是一件正在进行的工作
-XX:UseAVX

分层编译的权衡

javac 不会影响性能
final关键字不会增加编译速度

GraalVM

插件技术生产二进制程序文件
通过对编译器重新构造C2编译器

预编译

提前编译(ahead-of-time compilation)

提前编译的一个好处是启动速度更快
这个设计再应用启动前进行编译将代码形成一个共享库,供jvm启动应用时使用,这意味着在应用程序启动阶段不需要引入即时编译

你可能感兴趣的:(Java性能指南,java,性能优化)