> For the complete documentation index, see [llms.txt](https://jun-wang.gitbook.io/learnjava/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://jun-wang.gitbook.io/learnjava/ji-shu-xue-xi/jvm-xue-xi/codecache.md).

# CodeCache

## 一、简介

JVM生成的native code存放的内存空间称之为Code Cache；JIT(即时编译器)编译、JNI(Java本地接口)等都会编译代码到native code，其中JIT生成的native code占用了Code Cache的绝大部分空间，codecache是一块独立于Java堆之外的内存区域。

JIT：Java程序最初是仅仅通过解释器解释执行的，即对字节码逐条解释执行，这种方式的执行速度相对会比较慢，尤其当某个方法或代码块运行的特别频繁时，这种方式的执行效率就显得很低。于是后来 在虚拟机中引入了JIT编译器（即时编译器），当虚拟机发现某个方法或代码块运行特别频繁时，达到某个阈值，就会把这些代码认定为“Hot Spot Code”（热点代码），为了提高热点代码的执行效率，在运行时，虚拟机将会把这些代码编译成与本地平台相关的机器码，并进行各层次的优化，完成这项任务的正是JIT编译器。

## 二、作用

codecache用来将热点代码缓存起来，加快代码运行速度，服务启动之后，随着时间的推移，肯定会有越来越多的方法被JIT编译成本地机器码，并存放到Code Cache，由于Code Cache大小是固定的，那么就存在被用完的风险。一旦Code Cache被填满，就会出现下面情况：

* JVM的JIT功能会被停止，将不会编译任何额外的代码。
* 被编译过的代码仍然以编译方式执行，但是尚未被编译的代码只能以解释方式执行。

这种情况下，如果应用中还有很多代码以解释方式执行，其性能会大大降低。

在Speculative flushing开启的情况下，当Code Cache不足时：

* 最早被编译的一半方法将会被放到一个old列表中等待回收；
* 在一定时间间隔内，如果old列表中方法没有被调用，这个方法就会被从Code Cache清除；

很不幸的是，在JDK1.7中，Speculative flushing释放了一部分空间，但是从编译日志来看，JIT并没有恢复正常，并且系统整体性能下降很多，出现了大量超时。

在Oracle官网上看到这样一个Bug：[http://bugs.java.com/bugdatabase/view\_bug.do?bug\_id=8006952](https://link.zhihu.com/?target=https%3A//links.jianshu.com/go%3Fto%3Dhttp%3A%2F%2Fbugs.java.com%2Fbugdatabase%2Fview_bug.do%3Fbug_id%3D8006952) 由于算法问题，当Code Cache不足之后会导致编译线程无法继续，并且消耗大量CPU，导致系统运行变慢。

## 三、相关参数

```
# 查看JVM设置的codecache最大大小
jinfo -flag ReservedCodeCacheSize {pid}
# 或者
java -XX:+PrintFlagsFinal|grep --color CodeCache

# JVM启动参数
# 设置初始CodeCache大小
-XX:InitialCodeCacheSize

# 设置Reserved code cache的最大大小，通常默认是240M
-XX:ReservedCodeCacheSize=240M

# 是否在code cache满的时候先尝试清理一下，如果还是不够用再关闭编译，默认开启
-XX:+UseCodeCacheFlushing

# 关闭分层编译
-XX:-TieredCompilation
```

*如果需要在运行时查看codecache大小，需要写段代码使用jmx查看，具体代码可以在最后的参考里面查看*

## 四、codecache回收策略

如果满足以下条件之一则进行回收：

1）codecache满了；

2）从上次扫描到现在，存在codecache最大值1%的“状态变更的代码”；

**状态变更？**

* alive -> not\_entrant（存活->新进入）
* not\_entrant -> zombie（新进入->僵尸）
* zombie -> marked\_for\_reclamation（僵尸->被标记为回收）

3）从上次扫描到一段时间内，codecache剩余的越多，越不容易触发回收。

回收会清理掉所有僵尸代码。如果 ***UseCodeCacheFlushing*** 打开的话还会清理非热点的存活代码。

**非热点的存活代码？**

* 在每个safepoint每个活动的栈帧的热度值被置为默认值【2 \* （codecache大小 / 1MB）】
* 每次清理会将热度值-1
* 当热度值小于一个阈值的时候会被判断为“非热点的存活代码”进而被清理，这个阈值和codecache的剩余百分比以及参数 ***-XX:NmethodSweepActivity*** 有关（默认值10），NmethodSweepActivity越大，codecache剩余百分比越小，那就越会被回收。

总结：codecache的清理时机和清理大小是在运行时动态决定的，影响因素包括：codecache总大小、剩余百分比，僵尸代码的数量，非热点存活代码的数量以及JVM的参数等。

> 参考：
>
> <https://juejin.cn/post/6844903601786060808>
>
> <https://blog.csdn.net/Rylan11/article/details/81606119>
>
> <https://www.jianshu.com/p/4214df5df334>
>
> <https://blog.csdn.net/weixin_41947378/article/details/113919808>
>
> <https://stackoverflow.com/questions/38173592/oracle-jvm-8-when-codecache-flushing-is-enabled-how-much-is-flushed>
>
> <https://titanwolf.org/Network/Articles/Article?AID=7b7b5ba8-204b-4f76-87f1-33d94c0dfa0c#gsc.tab=0>
>
> <https://blog.csdn.net/bear_lam/article/details/79400657>
>
> <https://www.jianshu.com/p/b9a5cbef12f2>
>
> <http://zhongmingmao.me/2019/01/02/jvm-advanced-jit/>
>
> <https://tech.meituan.com/2020/10/22/java-jit-practice-in-meituan.html>
>
> <https://www.codenong.com/cs107067700/>
