4.7 分层编译级别

程序使用分层编译时,编译日志中会输出代码所编译的分层级别。上一节的示例中,代码最多编译到级别 4,甚至为了简化讨论,到目前为止,我已经说过只有两种编译器(加上解释器)。

因为 client 编译器有 3 种级别,所以总共有 5 种执行级别。因此,编译级别有:

  • 0:解释代码

  • 1:简单 C1 编译代码

  • 2:受限的 C1 编译代码

  • 3:完全 C1 编译代码

  • 4:C2 编译代码

典型的编译日志可以显示,多数方法第一次编译的级别是 3,即完全 C1 编译。(当然,所有方法都从级别 0 开始。)如果方法运行得足够频繁,它就会编译成级别 4(级别 3 的代码就会被丢弃)。最常见的情况是:client 编译器从获取了代码如何使用的信息进行优化时才开始编译。

如果 server 编译器队列满了,就会从 server 队列中取出方法,以级别 2 进行编译,在这个级别上,C1 编译器使用方法调用计数器和回边计数器(但不需要性能分析的反馈信息)。这使得方法编译得更快,而方法也将在 C1 编译器收集分析信息之后被编译为级别 3,最终当 server 编译器队列不太忙的时候被编译为级别 4。

另一方面,如果 client 编译器全忙,原本排程在级别 3 编译的方法就既可以等待级别 3 编译,也适合进行级别 4 的编译。在这种情况下,方法编译会很快转到级别 2,然后由级别 2 转到级别 4。

那些不太重要的方法可以从级别 2 或级别 3 开始编译,但随后会因为它们的重要性没那么高而转为级别 1。另外,如果 server 编译器出于某些原因无法编译代码,也会转为级别 1。

当然,代码在逆编译时会转为级别 0。

有些标志可以控制某些级别转换行为,但调优能够得到很乐观的结果。当方法按期望的顺序,即级别 0 → 级别 3 → 级别 4 编译时,性能可以达到最优。如果方法经常被编译为级别 2,并且还额外有可用的 CPU 周期,那就可以考虑增加编译器的线程数,从而减少 server 编译器队列的长度。如果没有额外可用的 CPU 周期,那你唯一能做的就是尽力减小应用的大小。

4.7 分层编译级别 - 图1 快速小结

1. 分层编译可以在两种编译器和 5 种级别之间进行。

2. 不建议人为更改级别,本节仅仅是辅助解释编译日志的输出。