设小ReservedCodeCacheSize会触发JIT硬性熔断,新方法退化解释执行致性能跌5–10倍;需监控jstat -compiler/-codecache及日志信号,按实测峰值上浮30%~50%设置,并启用UseCodeCacheFlushing等防护。

保留代码缓存大小(-XX:ReservedCodeCacheSize)设小会直接触发 JIT 编译器“硬性熔断”——不是变慢,而是彻底停编。新方法(Lambda、Spring 代理、反射调用路径)全部退化为解释执行,性能常跌 5–10 倍。这不是 GC 问题,jstat -gc 看不出异常,线索全在编译器自身状态里。
盯住三个关键信号确认是否已熔断
别等服务卡顿再排查,要主动监控:
-
jstat -compiler <pid>:Compiled 字段长期不动,Failed 持续上涨(每分钟超 100 次即高危),Invalid 明显变大 -
jstat -codecache <pid>: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 <pid> 1000 5每秒采样 5 次,抓高峰 Used 值 - 按峰值上浮 30%~50% 设定,例如峰值 180MB → 推荐
-XX:ReservedCodeCacheSize=256m(单位必须带小写 m) - JDK 8u202 之前默认仅 48MB,老服务风险极高;JDK 11+ 默认 240–256MB,仍需实测
- 容器部署时,这部分内存要算进总预算,它属于 off-heap,不受
-Xmx控制
只调大参数不够,必须组合防护
Code Cache 是连续内存块,盲目堆大会引发新问题:
- 设为 1G+ 容易触发 mmap 分配失败,尤其在容器内存受限时
- 过大缓存拖慢 JIT 内部 hash 表查找,反而降低编译吞吐
- 可能挤压 Compressed Class Space,诱发元空间 OOM
- 真正瓶颈常是碎片化:还有空闲字节,但最大连续空闲块趋近于 0
必须配合启用:
-
-XX:+UseCodeCacheFlushing:在即将填满时主动驱逐冷代码 -
-XX:+UseG1GC:对 Code Cache 元数据管理更友好,降低碎片概率 -
-XX:CodeCacheMinimumFreeSpace=32m:设定安全水位线,提前触发清理
从源头减少 Code Cache 消耗
动态代理、Lambda、频繁 redefine 是主要“吃缓存大户”:
- Spring AOP 中避免无必要启用
proxy-target-class=true(即 CGLIB),优先用 JDK Proxy - 禁止在循环或监听器中反复调用
Proxy.newProxyInstance或Enhancer.create - 避免在循环内反复创建 Function 实例,改用静态方法引用
- 对高频反射调用(如 JSON 序列化)启用白名单缓存,减少 JIT 频繁重编译压力


















