bigywater.tech
Java · 243

Code Cache:JIT 生成的机器码放在哪里

JIT 编译出来的机器码放在代码缓存里。它有多大、怎么分段、满了会怎样?这篇用 jcmd 看真实的分段使用量,再故意把它填满,量出 JIT 被关掉后新热点的代价。

240 MB / 3 段
默认的代码缓存大小与分段
8.89 → 240.13 ns
全新热方法每次调用:正常 → 代码缓存满且 JIT 被关闭
刷新
6 MB 时 UseCodeCacheFlushing 开着没有填满,关掉才填满

分段的代码缓存

实测 · 本机执行输出
  [默认]
    CodeHeap 'non-profiled nmethods': size=120032Kb used=57Kb max_used=57Kb free=119975Kb
    CodeHeap 'profiled nmethods': size=120016Kb used=151Kb max_used=151Kb free=119864Kb
    CodeHeap 'non-nmethods': size=5712Kb used=1241Kb max_used=1256Kb free=4470Kb
    CodeCache: size=245760Kb, used=1449Kb, max_used=1464Kb, free=244309Kb
    total_blobs=527, nmethods=139, adapters=297, full_count=0
  [-XX:TieredStopAtLevel=1(只用 C1)]
    CodeCache: size=49152Kb used=1883Kb max_used=1883Kb free=47268Kb
    total_blobs=505, nmethods=136, adapters=297, full_count=0
  [-XX:-SegmentedCodeCache]
    CodeCache: size=245760Kb used=1465Kb max_used=1478Kb free=244294Kb
    total_blobs=528, nmethods=140, adapters=297, full_count=0

填满之后会怎样

实测 · 本机执行输出
  [默认(ReservedCodeCacheSize = 240 MB)] 返回码 0
    先测一次基准方法:9.43 ns/次
    编译了 2500 个热方法之后,早就编译好的 bench 仍是:[9.57, 9.44, 9.44] ns/次
    之后第一次用到的全新热方法 late(与 bench 同样规模的循环):第 1 轮 8.89 ns/次,第 2 轮 8.38 ns/次
  [-XX:ReservedCodeCacheSize=6m] 返回码 0
    先测一次基准方法:9.43 ns/次
    编译了 2500 个热方法之后,早就编译好的 bench 仍是:[9.43, 9.45, 9.42] ns/次
    之后第一次用到的全新热方法 late(与 bench 同样规模的循环):第 1 轮 9.90 ns/次,第 2 轮 8.41 ns/次
  [-XX:ReservedCodeCacheSize=6m -XX:-UseCodeCacheFlushing] 返回码 0
    先测一次基准方法:9.44 ns/次
    [3.218s][warning][codecache] CodeCache is full. Compiler has been disabled.
    [3.218s][warning][codecache] Try increasing the code cache size using -XX:ReservedCodeCacheSize=
    CodeCache: size=6144Kb used=6117Kb max_used=6132Kb free=26Kb
    编译了 2500 个热方法之后,早就编译好的 bench 仍是:[9.48, 9.53, 9.43] ns/次
    之后第一次用到的全新热方法 late(与 bench 同样规模的循环):第 1 轮 240.13 ns/次,第 2 轮 239.27 ns/次
    OpenJDK 64-Bit Server VM warning: CodeCache is full. Compiler has been disabled.
    OpenJDK 64-Bit Server VM warning: Try increasing the code cache size using -XX:ReservedCodeCacheSize=

相关参数的默认值

实测 · 本机执行输出
  ReservedCodeCacheSize        = 251658240(240.0 MB)
  InitialCodeCacheSize         = 2555904(2.4 MB)
  NonNMethodCodeHeapSize       = 5839564(5.6 MB)
  NonProfiledCodeHeapSize      = 122909338(117.2 MB)
  ProfiledCodeHeapSize         = 122909338(117.2 MB)
  SegmentedCodeCache           = true
  UseCodeCacheFlushing         = true
  CodeCacheExpansionSize       = 65536
  ReservedCodeCacheSize        = 251658240(240.0 MB)

分步演示

代码缓存
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12,10 核 macOS;2500 个生成的热方法(每个 700 次调用 × 200 次循环)
    • 没有验证:Linux 上的默认大小、代码缓存碎片、-XX:+PrintCodeCache 的详细字段、代码缓存刷新(flushing)具体清理了哪些方法(只观察到开着时没有填满)、JDK 17 与 27 的差别、热点方法太多时的 deopt 行为(见相关篇目)
    京ICP备2026067502号-1京公网安备11010502063487号