1.概览1.1.即时编译器是Java虚拟机的核心1.1.1.just-in-time compiler,简称JIT compiler1.1.2.即时编译器会频繁地使用寄存器1.2.编译型语言1.2.1.程序是以二进制(编译后的)代码的形式发布的1.2.1.1.汇编代码是针对特定CPU的1.2.1.2.兼容的CPU可以执行同一个二进制文件1.2.2.如C++和Fortran1.3.解释型语言1.3.1.同样的程序源代码就可以在任何CPU上运行1.3.2.如PHP和Perl1.4.中间地带1.4.1.编译为一种中间的低级语言1.4.1.1.Java字节码1.4.1.2.由JVM进一步编译成汇编语言1.4.2.利用了脚本语言的平台独立性和编译型语言的原生性能1.4.3.在代码执行时将其编译为平台相关的二进制代码1.4.3.1.是"即时"的1.5.热点编译1.5.1.只有一小部分代码会频繁执行,应用程序的性能则主要取决于这些代码执行的快慢1.5.2.关键代码被称为应用程序的热点1.5.2.1.这部分代码执行得越多,就说这部分代码越热1.5.3.只执行一次的代码,解释执行Java字节码会更快一点1.5.4.代码是频繁调用的方法或者迭代多次的循环,编译它就是值得的2.分层编译2.1.现在所有JVM都在使用的技术2.2.-XX:-TieredCompilation标志2.2.1.默认值是true2.3.两种类型2.3.1.client编译器2.3.1.1.C1编译器12.3.2.server编译器2.3.2.1.C2编译器22.4.编译代码的时机2.4.1.基于程序会运行多长时间和程序的启动时间有多重要2.4.2.C1编译器比C2编译器更早开始编译2.4.3.在代码执行的开始阶段,C1编译器的速度更快2.4.4.C2编译器在等待时获得了信息,这些信息让C2编译器能够对编译后的代码进行更好的优化3.编译器标志3.1.代码缓存3.1.1.一种设定了最大值的系统资源3.1.2.影响JVM可以运行的编译后的代码总量3.1.3.最大值3.1.3.1.-XX:ReservedCodeCacheSize=N3.1.3.2.默认240 MB3.1.4.初始大小3.1.4.1.-XX:InitialCodeCacheSize=N3.1.4.2.默认2496 KB3.2.Java 11中3.2.1.非方法代码(nonmethod code)3.2.1.1.-XX:NonMethodCodeHeapSize=N3.2.2.性能分析代码(profiled code)3.2.2.1.-XX:ProfiledCodeHeapSize=N3.2.3.非性能分析代码(nonprofiled code)3.2.3.1.-XX:NonProfiledCodeHeapSize=N3.3.检查编译过程3.3.1.不是用来优化的,它不会提升应用程序的性能3.3.2.-XX:+PrintCompilation标志3.3.2.1.默认为false3.3.3.attributes字段由5个字符构成,表示正在编译的代码状态3.3.3.1.%3.3.3.1.1.栈上替换(on-stack replacement,OSR)3.3.3.2.s3.3.3.2.1.方法是同步的3.3.3.3.!3.3.3.3.1.方法有异常处理器3.3.3.4.b3.3.3.4.1.在阻塞模式下发生的编译3.3.3.5.n3.3.3.5.1.原生方法封装时发生的编译4.分层编译级别4.1.如果没有额外的CPU周期可用,你能做的就是尽量缩减应用程序的大小4.2.C1编译器有3个级别,所有总共有5个编译级别4.2.1.04.2.1.1.解释代码4.2.2.14.2.2.1.简单C1编译代码4.2.3.24.2.3.1.受限C1编译代码4.2.4.34.2.4.1.完全C1编译代码4.2.5.44.2.5.1.C2编译代码4.3.C1编译器得到代码是如何使用的信息之后,会利用这些信息进行优化,然后才开始编译4.4.如果C2编译器队列已满,那么队列中的方法会被取出,在级别2上编译4.5.如果C1编译器队列已满,那么计划在级别3上编译的方法在等待编译的同时,可以进行级别4的编译4.5.1.它会被快速编译到级别2,然后转到级别44.6.不重要的方法可以从级别2或者级别3开始编译4.6.1.它们本质上不那么重要,所以会回到级别14.7.如果代码发生了逆优化,就会回到级别05.逆优化(deoptimization)5.1.编译器不得不"撤销"之前的编译5.1.1.JVM替换之前编译的代码的过程5.2.发生情况5.2.1.代码被丢弃(made not entrant)5.2.2.产生僵尸代码(made zombie)5.3.代码被丢弃的原因5.3.1.类和接口的工作方式5.3.1.1.逆优化对性能并没有太大影响5.3.2.分层编译的工作方式5.3.2.1.当代码被C2编译器编译时,JVM必须替换已经被C1编译器编译的代码5.3.2.2.它将旧代码标记为丢弃,并用逆优化机制替换为新编译的(更高效的)代码5.3.2.3.让代码变得更快5.4.逆优化僵尸代码5.4.1.僵尸代码的小规模重新编译,不会对大多数应用程序产生显著的影响