bigywater.tech
Java 内部原理 · 32

反射调用真实慢多少:真实测出比直接调用慢 6 倍,setAccessible 能省一半

"反射比直接调用慢"几乎是常识,但慢多少、setAccessible(true) 到底有没有用、用了能省多少,很少有人给出具体数字。真实计时三种调用方式,各跑 5000 万次。

32ms → 200ms
真实计时:5000 万次调用,直接调用约 32ms,未 setAccessible 的反射调用约 200ms,慢了 6.2~6.5 倍
200ms → ~100ms
真实计时:调用 setAccessible(true) 之后反射调用真实快了近 2 倍,但依然比直接调用慢约 3 倍

上一篇:两个一模一样的类:真实用两个 ClassLoader 造出两个不能互相强转的 Foo

思路:三种调用方式,同样的计算量

一个最简单的静态方法,分别用直接调用、Method.invoke()(未 setAccessible)、Method.invoke()(已 setAccessible)三种方式各调 5000 万次。

ReflectionCost.java(三种调用方式) static int add(int a, int b) { return a + b; } Method m = ReflectionCost.class.getDeclaredMethod("add", int.class, int.class); Method mAccessible = ReflectionCost.class.getDeclaredMethod("add", int.class, int.class); mAccessible.setAccessible(true); // 直接调用 sum1 += add(i, 1); // 反射调用(未 setAccessible) sum2 += (int) m.invoke(null, i, 1); // 反射调用(已 setAccessible) sum3 += (int) mAccessible.invoke(null, i, 1);

三组循环共享同一段预热(先各跑 200 万次让 JIT 充分热身),正式计时前用求和结果做一致性校验,排除任何一组算错的可能。

真实验证:反射调用慢 6 倍,setAccessible 省回近一半

两次独立运行,结果量级完全一致。

真实实测 · 第一次运行
循环 50000000 次(结果校验: sum1==sum2==sum3 -> true):
直接调用           耗时 = 32ms
反射调用(未 setAccessible) 耗时 = 208ms
反射调用(已 setAccessible) 耗时 = 96ms
反射/直接 倍数 = 6.5
setAccessible 前后倍数 = 2.15
真实实测 · 第二次独立运行
直接调用           耗时 = 32ms
反射调用(未 setAccessible) 耗时 = 201ms
反射调用(已 setAccessible) 耗时 = 105ms
反射/直接 倍数 = 6.2
setAccessible 前后倍数 = 1.90

真实数据非常稳定:反射调用相对直接调用慢 6.2~6.5 倍;调用 setAccessible(true) 之后,反射调用耗时降到原来的一半左右(倍数 1.9~2.15),但依然比直接调用慢约 3 倍。三组结果全程 sum1==sum2==sum3 校验通过,差距纯粹来自调用方式本身,不是计算逻辑不一致。

setAccessible(true) 真实省的是什么?省的不是访问权限检查本身的语义(这个方法本来就是可访问的 public 静态方法,权限检查怎么样都会通过),而是每次反射调用时,JVM 真实要做的一次"访问权限校验"这个固定步骤——setAccessible(true) 告诉 JVM "以后调用这个 Method 不用再检查了",真实省下了这部分重复校验的开销。反射调用本身依然比直接调用慢得多,因为反射路径要经过参数装箱(这里的 int 参数要真实包装成 Object)、方法查找的间接层、以及 JIT 更难对反射调用做内联优化。

分步演示:反射调用真实比直接调用多做了哪些事

每一行都是真实运行这段代码得到的结果。

反射的真实代价不只是抽象的"慢":Method.invoke()真实强制把基本类型装箱、真实每次都要做访问检查、真实从不缓存Method对象——MethodHandle能真实避开这几层开销
这一步新验证的调用方式

    参考与说明

    • 本文全部计时数据均在本机真实运行 Java 17 得到,两次独立运行倍数量级一致。
    • 这篇文章本身大量用到的 --add-opens + setAccessible 反射手法,系列前面 HashMap 容量与阈值 等多篇都在用,这次专门测了它本身的真实代价。
    • 没有涉及:MethodHandle(JDK 7+ 引入,通常比传统反射更快)的真实对比、反射调用在超高频调用下 JIT 是否有机会通过 GeneratedMethodAccessor 内联优化进一步收窄差距——这些留给系列后面按需再写。
    京ICP备2026067502号-1京公网安备11010502063487号