java性能优化--逆优化和分层编译级别

687 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天,点击查看活动详情

在前面的多个章节中,讲解了关于JIT编译器的相关内容,有些细节的东西没有讲,所以本章节针对前面忽略的内容来讲一下,首先先讲一下关于逆优化

什么是逆优化?

前面我们讲解如何查看jvm被编译的代码时,使用了参数PrintCopilation,然后可以在日志当中看到被编译的方法:

image.png

在上图的内容当中,有一个忽略的字段没有讲解,只提到叫做逆优化。 它有两个值:

  • made not entrant :代码被丢弃
  • made zombie :产生僵尸代码

发生逆优化,意味着编译器需要撤销之前的某些编译,这会导致性能下降,知道下次重新编译为止。

made not entrant 代码被丢弃

产生代码被丢弃通常有两种原因:

  • 之前优化编译的代码不在有效:代码执行条件或逻辑的改变,导致代码重新编译。
  • 分层编译:server编译代码替换client编译的代码,将老代码编辑为被丢弃;新编译的代码,或性能够好的代码替换旧的代码。

made zombie 产生僵尸代码

jvm通过GC回收前面标记位被丢弃的代码,此时这些代码将会标记位僵尸代码。

这样的好处是将已经编译的被丢弃的代码,从代码缓存当中回收掉,保证代码缓存空间能够被其他编译代码利用。

如果被标记位僵尸代码的代码,被重新频繁使用,JVM需要重新编译和优化代码。但是想僵尸代码被重新编译优化这样的方式,通常不会产生性能上的影响。

什么是分层编译级别?

在上一小节的图中,还有一个叫做分层编译级别的参数没有讲解,下面一起来看下。

使用分层编译时,在编译日志中会打印出由数字 0 ~ 4 的分层编译级别。

分层编译级别描述
0解释代码
1简单C1编译代码
2受限的C1编译代码
3完全的C1编译代码
4C2编译代码

我们前面提到过,client编译器被称为C1编译器,而server编译器被称为C2编译器。

  • 方法编译从0开始。
  • 大多数方法的编译级别都是3
  • 运行足够频繁的代码,编译级别会变成4,原本的3将被丢弃
  • 如果serve编译队列满了,取出方法按照级别2进行编译。C1编译器此时调用方法调用计数器和回边计数器,更快的完成编译,当C1收集完分析信息后,将其编译成3,当server编译队列不忙额时候,再将其编译成4。
  • 如果C1很忙的时候,代码可以等待级别3,也可以先用4编译,但是是先编译为2,在编译成4。
  • 不重要的方法可以从2或3编译,后续转为1.
  • server无法编译的也会转为1.
  • 你编译转为0.