jvm|JVM底层原理之什么是分层编译?

jvm|JVM底层原理之什么是分层编译?

文章图片


JVM底层原理之什么是分层编译?
什么是分层编译?从JDK6开始 , Java引入了分层编译 , 这种方式综合了 C1 和 C2 的优势 , 不过这时它还只是初步的实现 。
而到JDK7才开始应用到server模式中 , 而server模式也常常是一些硬件系统的默认选择 , 所以也可看做是JDK7默认推荐使用分层编译了 。 当然我们还是可以通过前面的手动指令去 【-client】或【-server】强制选择 。
分层编译将 JVM 的执行状态分为了 5 个层次:

  • level 0:解释器解释执行 , 默认开启profiling 。
  • level 1:C1 编译 , 执行不带profiling的C1代码 , 不开启 profiling 。
  • level 2:C1 编译 , 仅执行带方法调用次数和循环回边执行次数 profiling 的 C1 代码 , 开启部分profiling 。
  • level 3:C1 编译 , 执行带所有 Profiling 的 C1 代码 , 开启 profiling 。
  • level 4:C2 编译 , 执行C2的代码 , C2编译也是将字节码编译为本地代码 , 但是会启用一些编译耗时较长的优化 , 甚至会根据性能监控信息进行一些不可靠的激进优化 。
profiling(性能监控):其中profiling就是收集能够反应程序执行状态的监控数据 , 它由多项数据组成 , 其中最基本的就是方法的调用次数和循环回边的执行次数(这块内容后面小节会详解) 。
各层次执行效率:虽然分了5个层次 , 但其实也没那么复杂 , 我们可以把level 123看做是一层:C1 , 因为这三层都是执行进行C1编译后的代码 , 唯一的区别是带了多少的profiling 。 因为profiling是影响性能的 , 所以执行效率上:level1>level2>level3 。
那有的人会觉得既然level1是执行C1代码的三个层次中 , 执行效率最高的 , 那为什么不一直执行level1呢?
当然 , level1因为没有开启profiling , 执行效率最高 , 但是这仅限于C1 , 如果是C2编译的代码 , 通常要比C1代码的执行效率高出**30% **!!而profiling , 可以理解为就是开启C2编译的条件或是钥匙 。
终止状态:level1由于放弃了钥匙 , 自然也失去了被编译为C2代码的机会 , 所以我们可以得知 , level1和level4是属于终止状态的 。 当一个方法执行到终止状态后 , 除非编译后的代码失效了 , 否则它就不会再次发出该方法的编译请求了 。 也就是一旦方法执行到level1和level4 , 一般它就固定下来了 , 后面不出意外也会一直执行level1或level4的层次 。
分层编译的触发关系:但是不管是什么方法 , 它都是从level 0开头的 , 这层是解释器 , 不进行任何编译 。
下图是几种常见的编译路径 , 我们一边看图一边分析 , 该图片借用自公众号文章:【基本功 | Java即时编译器原理解析及实践】https://mp.weixin.qq.com/s/7PH8o1tbjLsM4-nOnjbwLw

①第一条路径是一般情况 , 热点方法从解释执行 , 到被第3层的C1编译 , 最后进入C2编译 。
②第二种情况是trivial method , 即不重要的方法 , 例如常见的getset方法 , 第3层的profiling没有收集到有价值的数据 , JVM就会放弃对其进行C2编译 , 转而进行无profiling的C1编译 。 因为差别并不大 , 还不如把宝贵的资源留给更需要进行C2编译的方法 。
③当C1忙碌的情况下 , 解释器在解释执行的过程中profiling , 然后直接跳转到C2编译 。
④当C2忙碌的情况下 , 由于第3层C1又比第2层慢不少 , 为了不浪费资源 , 先进行第2层的C1 , 再随着时间进行第3层的C1编译 。 最后等C2不忙了 , 才进入C2编译 。 没错 , 这条线路不会进入第1层C1 , C2再忙也不能直接把可能需要优化的方法简单的用C1处理了就完事了 。