code cache sweeper 在三种条件下触发:codecache 耗尽、显式启用 usecodecacheflushing 并满足老化/热度阈值、jvm 执行 vm_gc_operation 类操作;默认仅满时强制清理,属救火式响应。

Code Cache Sweeper 触发条件有哪些
Code Cache Sweeper 不是定时轮询,而是由特定事件驱动的后台线程。它只在以下三种情况之一发生时才真正工作:CodeCache is full、UseCodeCacheFlushing 被显式启用且满足老化/热度阈值、或 JVM 主动执行 VM_GC_Operation 类操作(如某些 GC 会捎带触发清理)。默认情况下(-XX:-UseCodeCacheFlushing),Sweeper 几乎不干活;只有 Code Cache 真的满了,才会强制触发一次清理 —— 但此时 JIT 已被禁用,属于“救火式响应”,不是预防性策略。
为什么长期运行后性能会悄悄变差
性能回归往往不是突然断崖,而是渐进式劣化,核心原因在于 Sweeper 的清理逻辑与真实热点分布错位:
• 它优先淘汰“年龄大但近期未调用”的 nmethod,但有些方法虽调用间隔长(比如定时任务每 5 分钟跑一次),实际仍是业务关键路径,一删就导致下次执行又得解释+重编译;
• 基于使用频率的淘汰需要统计开销,默认关闭(-XX:-ProfileInterpreter 时更不可靠),Sweeper 实际依赖的是粗粒度的“上次调用时间戳”,容易把冷热混合的方法误判为冷代码;
• 分层编译(-XX:+TieredCompilation)下,C1 编译的快但占空间小,C2 编译的优但体积大,Sweeper 清理时不会区分层级,常把已优化的 C2 nmethod 和未优化的 C1 版本一起踢出,后续只能降级回 C1 或解释执行。
如何确认当前 Sweeper 是否在起作用
不能只看 PrintCodeCache 日志里有没有“swept”字样,那只是结果;要抓过程,得开更细粒度日志:
• 启动时加 -XX:+UnlockDiagnosticVMOptions -XX:+PrintCodeCacheOnCompilation -XX:+LogCompilation,配合 hslog 分析文件中 codecache_sweep 事件的时间戳和清理量;
• 运行中用 jcmd <pid> VM.native_memory summary</pid> 查看 [code] 区域的 committed vs. used 波动,若 committed 稳定但 used 频繁触顶回落,说明 Sweeper 在反复介入;
• 注意 PrintGCDetails 日志里是否夹杂 CodeCache: before sweep 和 after sweep 行 —— 这是唯一能证明 Sweeper 主动工作的证据,不是满才扫,而是扫了才可能避免满。
调整 Sweeper 行为的关键参数组合
单靠增大 -XX:ReservedCodeCacheSize 只是拖延问题,真正可控的是它的“脾气”:
• -XX:+UseCodeCacheFlushing 必须开启,否则 Sweeper 永远懒加载;
• -XX:CodeCacheMinimumFreeSpace=2m 设一个安全水位(默认 480KB 太低),低于此值就提前触发清理,避免等满才动手;
• -XX:CodeCacheFlushingThreshold=50 控制每次清理上限(百分比),防止一次清太多导致批量回退;
• 配合 -XX:TieredStopAtLevel=1 可限制只用 C1 编译,大幅降低单个 nmethod 体积,让 Sweeper 清理更“温柔”。
CodeCache is full… The compiler has been disabled 才行动。










