Java JEP 169文档中的out-of-line call是什么含义?有对应代码示例吗?
什么是out-of-line call
out-of-line call(非内联调用)是与内联调用(inline call)相对的概念:JIT编译器的内联优化会把被调用方法的代码直接展开插入到调用方的执行流中,不需要执行跳转指令;如果方法无法被内联,就必须走常规的方法调用流程,跳转到对应方法的独立代码块执行,这类调用就属于out-of-line call。
对标量化优化的影响
标量化(将对象拆分为字段直接存在栈上、消除堆分配)的前提是JIT能完整追踪对象的所有使用场景,确认不需要对外暴露对象的引用地址、也不会用到对象的身份语义(比如==比较、hashCode计算)。
如果调用的是out-of-line方法,JIT在编译当前方法时看不到被调用方法的内部逻辑,无法确认传入的对象引用会不会被存储逃逸、会不会被用于身份相关操作,因此必须把对象完整分配到堆上,将引用传递给被调用方法,遵守Java的标准引用语义,标量化优化就会失效。
代码示例
支持标量化的无out-of-line call场景
class Point { private final int x; private final int y; public Point(int x, int y) { this.x = x; this.y = y; } // 小体积 getter 会被JIT自动内联 public int getX() { return x; } public int getY() { return y; } } public int calculate() { Point p = new Point(1, 2); // getX、getY都是内联调用,没有out-of-line调用 return p.getX() + p.getY(); }
该场景下Point对象会被标量化为两个int栈变量,不会产生堆分配。
触发out-of-line call的失效场景
// 示例:体积过大、被标记为不内联、或者是无法确定实现的虚方法,都会导致无法内联 public void handlePoint(Point p) { // 内部可能使用对象引用做身份比较、存储到全局缓存等操作 if (p == globalPointCache.getIfPresent(p.hashCode())) { triggerLogic(); } globalPointCache.put(p); } public void testInvoke() { Point p = new Point(3, 4); // handlePoint无法被内联,属于out-of-line call handlePoint(p); }
该场景下handlePoint是out-of-line调用,JIT无法确认p的使用范围,因此必须在堆上分配完整的Point对象,无法执行标量化。
常见的out-of-line call场景
- 方法体积超过JIT内联阈值(默认高频方法最大内联体积为325字节)
- 方法是虚方法,编译期无法确定实际调用的实现类
- 调用native方法、外部依赖的未编译方法
- 被
@DontInline注解标记的方法
内容的提问来源于stack exchange,提问作者Shankha057
相关产品推荐
相关产品推荐

