reservedcodecachesize过小会导致jit编译器硬性禁用,新方法全部退化解释执行、性能下降5–10倍;需通过jstat -compiler和jstat -codecache观察compiled停滞、failed上升、largest_free_block<1mb及日志“codecache is full”确认,并按实测峰值上浮30%~50%配置。

ReservedCodeCacheSize 设置过小,会直接导致 JIT 编译器“硬性熔断”——不是变慢,而是彻底停编。新方法(Lambda、Spring 代理、反射调用路径)全部退化为解释执行,性能常跌 5–10 倍。这不是 GC 问题,jstat -gc 看不出异常,线索全在编译器自身状态里。
怎么看是不是 Code Cache 溢出?
别猜,盯三个关键信号:
-
jstat -compiler
:Compiled 字段长期不动,Failed 持续上涨(每分钟超 100 次即高危),Invalid 明显变大 -
jstat -codecache
:Used 接近 Max,Unallocated Capacity 长期为 0;注意看 largest_free_block —— 若低于 1MB,即使 Used 只有 70%,JIT 也大概率已禁用 - 日志关键词:“CodeCache is full. The compiler has been disabled” 或 “hotspotcompilequeue: disabled”,这是熔断事实,不是警告
怎么设 ReservedCodeCacheSize 才合理?
不能拍脑袋定 512m 或 1g,得按实测峰值加余量:
- 用 jstat -codecache
1000 5 每秒采样 5 次,抓高峰 Used 值 - 按峰值上浮 30%~50% 设定,比如峰值 180MB → 推荐 -XX:ReservedCodeCacheSize=256m(单位必须带 m/M)
- 容器部署时,这部分内存要算进总预算,它属于 off-heap,不受 -Xmx 控制
- JDK 8u202 之前默认仅 48MB,老服务风险极高;JDK 11+ 默认 240–256MB,仍需实测
为什么只调大参数还不够?
Code Cache 是连续内存块,盲目堆大会引发新问题:
- 设为 1G+ 容易触发 mmap 分配失败,尤其在容器内存受限时
- 过大缓存拖慢 JIT 内部 hash 表查找,反而降低编译吞吐
- 可能挤压 Compressed Class Space,诱发元空间 OOM
- 真正瓶颈常是碎片化:还有空闲字节,但最大连续空闲块趋近于 0
必须配合的保护机制
单靠 ReservedCodeCacheSize 调大治标不治本,生产环境应组合启用:
- -XX:+UseCodeCacheFlushing:JDK 8u292+/11+ 需显式开启,当使用率 >80% 时主动驱逐冷代码(profiled 但未再调用的方法)
- -XX:+UseG1GC:G1 是目前唯一被充分验证能协同清理 CodeCache 的 GC;单独开 flushing 在 Parallel GC 下效果极弱
- 对动态代理密集型应用(如 Spring Cloud Gateway、MyBatis + AOP),可考虑 -XX:TieredStopAtLevel=3,禁用 C2 编译,保留 C1 的稳定性能










