持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天,点击查看活动详情
在前面的多个章节中,讲解了关于JIT编译器的相关内容,有些细节的东西没有讲,所以本章节针对前面忽略的内容来讲一下,首先先讲一下关于
逆优化。
什么是逆优化?
前面我们讲解如何查看jvm被编译的代码时,使用了参数PrintCopilation,然后可以在日志当中看到被编译的方法:
在上图的内容当中,有一个忽略的字段没有讲解,只提到叫做逆优化。
它有两个值:
made not entrant:代码被丢弃made zombie:产生僵尸代码
发生逆优化,意味着编译器需要撤销之前的某些编译,这会导致性能下降,知道下次重新编译为止。
made not entrant 代码被丢弃
产生代码被丢弃通常有两种原因:
- 之前优化编译的代码不在有效:代码执行条件或逻辑的改变,导致代码重新编译。
- 分层编译:server编译代码替换client编译的代码,将老代码编辑为被丢弃;新编译的代码,或性能够好的代码替换旧的代码。
made zombie 产生僵尸代码
jvm通过GC回收前面标记位被丢弃的代码,此时这些代码将会标记位僵尸代码。
这样的好处是将已经编译的被丢弃的代码,从代码缓存当中回收掉,保证代码缓存空间能够被其他编译代码利用。
如果被标记位僵尸代码的代码,被重新频繁使用,JVM需要重新编译和优化代码。但是想僵尸代码被重新编译优化这样的方式,通常不会产生性能上的影响。
什么是分层编译级别?
在上一小节的图中,还有一个叫做分层编译级别的参数没有讲解,下面一起来看下。
使用分层编译时,在编译日志中会打印出由数字 0 ~ 4 的分层编译级别。
| 分层编译级别 | 描述 |
|---|---|
| 0 | 解释代码 |
| 1 | 简单C1编译代码 |
| 2 | 受限的C1编译代码 |
| 3 | 完全的C1编译代码 |
| 4 | C2编译代码 |
我们前面提到过,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.