bigywater.tech
Java · 339

JIT 内联:方法调用如何变成调用者代码

JIT 说「内联」,具体内联到什么程度?多大的方法会被拒绝?调用点上有几种类型就不再内联了?这篇用 PrintInlining 的真实输出逐项核对,并用参数和耗时证实。

FreqInlineSize = 325
热调用点能内联的方法字节码大小上限(521 字节的 big 被拒绝,157 字节的被接受)
0.69 → 3.26 ns
同一个调用点接收者类型从 2 种增加到 3 种时每次调用的耗时
45.18 → 36.32 ns
把 FreqInlineSize 调到 1000、让 521 字节的方法也被内联后,每次迭代的耗时

方法大小

实测 · 本机执行输出
  各方法的字节码大小(约): {'tiny': 4, 'medium': 105, 'medium2': 157, 'big': 521, 'work': 50, 'callShapes': 40} ;MaxInlineSize = 35、FreqInlineSize = 325(PrintFlagsFinal 的默认值见下)
  默认值: {'FreqInlineSize': '325', 'InlineSmallCode': '2500', 'MaxInlineLevel': '15', 'MaxInlineSize': '35', 'MaxRecursiveInlineLevel': '1'}

C1 与 C2 的内联决定

实测 · 本机执行输出
  tier 3(C1 带 profile)编译 work() 时的内联决定:
    Main::tiny     inline | inline (hot)
    Main::medium   callee is too large | inline (hot)
    Main::medium2  callee is too large | inline (hot)
    Main::big      callee is too large | hot method too big
  tier 4(C2)编译 work() 时的内联决定:
    Main::tiny     inline (hot)
    Main::medium   inline (hot)
    Main::medium2  inline (hot)
    Main::big      hot method too big

改参数

实测 · 本机执行输出
  默认                           耗时  45.18 ns/次;内联决定:{'tiny': 'inline (hot)', 'medium': 'inline (hot)', 'big': 'hot method too big', 'medium2': 'inline (hot)'}
  -XX:FreqInlineSize=100       耗时  50.49 ns/次;内联决定:{'tiny': 'inline (hot)', 'medium': 'hot method too big', 'big': 'hot method too big', 'medium2': 'hot method too big'}
  -XX:FreqInlineSize=1000      耗时  36.32 ns/次;内联决定:{'tiny': 'inline (hot)', 'medium': 'inline (hot)', 'big': 'inline (hot)', 'medium2': 'inline (hot)'}
  -XX:MaxInlineSize=100        耗时  45.17 ns/次;内联决定:{'tiny': 'inline (hot)', 'medium': 'inline (hot)', 'big': 'hot method too big', 'medium2': 'inline (hot)'}
  -XX:-Inline(完全关闭内联)          耗时  49.94 ns/次;内联决定:{'tiny': 'not an accessor', 'medium': 'not an accessor', 'big': 'hot method too big', 'medium2': 'not an accessor'}

多态调用点

实测 · 本机执行输出
  1 种接收者类型:每次调用 0.51 ns;PrintInlining 里 area() 的行:['@ 23 Main$A::area (4 bytes) inline (hot)', '@ 23 Main$Shape::area (0 bytes) no static binding']
  2 种接收者类型:每次调用 0.69 ns;PrintInlining 里 area() 的行:['@ 23 Main$A::area (4 bytes) inline (hot)', '@ 23 Main$B::area (4 bytes) inline (hot)', '@ 23 Main$Shape::area (0 bytes) no static binding']
  3 种接收者类型:每次调用 3.27 ns;PrintInlining 里 area() 的行:['@ 23 Main$Shape::area (0 bytes) no static binding', '@ 23 Main$Shape::area (0 bytes) virtual call']
  4 种接收者类型:每次调用 3.26 ns;PrintInlining 里 area() 的行:['@ 23 Main$Shape::area (0 bytes) no static binding', '@ 23 Main$Shape::area (0 bytes) virtual call']

分步演示

JIT 内联
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12,Apple Silicon;-XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining -XX:+PrintCompilation;自写计时(不是 JMH),5 次取最小、3 个进程取中位数
    • 没有验证:JDK 27 的内联启发式、InlineSmallCode 与 MaxInlineLevel 的作用、递归内联、虚方法 + 类层次分析(CHA)对「只有一个实现」的接口的内联、逃逸分析与内联的配合、-XX:+PrintInlining 在 Linux / x86 上的输出差异
    京ICP备2026067502号-1京公网安备11010502063487号