反射调用比直接调用慢近90倍;主要开销来自运行时类型检查、元数据查找和无法JIT内联;Class.forName、getMethods、invoke等操作最伤性能,需缓存Class/Method并调用setAccessible(true)提升30%~50%,或改用MethodHandle、VarHandle、字节码生成等替代方案。

反射调用比直接调用慢多少?
不是“稍微慢一点”,而是量级差异:在创建 100 万个对象的基准测试中,new TestUser() 耗时约 10ms,而每次调用 Class.forName(...).newInstance()(无缓存)耗时约 926ms —— 慢了近 90 倍。方法调用、字段读写也类似,反射开销主要来自三块:运行时类型检查(权限、可见性)、元数据查找(遍历所有 method/field)、无法被 JIT 内联(编译器完全不知道你要调什么)。
哪些反射操作最伤性能?
以下操作在高频场景中要特别警惕:
-
Class.forName("xxx"):类加载+链接+初始化,开销最大,尤其带静态块时 -
clazz.getMethods()或clazz.getDeclaredFields():返回数组并遍历全部成员,哪怕你只想要一个id字段 -
method.invoke(obj, args)(未禁用安全检查):每次调用都做SecurityManager检查和参数包装(如将int装箱为Integer) - 反复调用
clazz.getMethod("setName", String.class)而不缓存Method对象
怎么缓存才真正有效?
缓存不是“把 Method 存进 HashMap 就完事”,关键在粒度和线程安全:
- 缓存
Class对象:用MyClass.class替代Class.forName("MyClass"),前者零开销;后者若必须用,至少缓存返回的Class实例 - 缓存
Method/Field:按Class + 方法名 + 参数类型数组作为 key,例如Map,其中Key = new Key(clazz, "setId", new Class[]{int.class}) - 务必调用
method.setAccessible(true)—— 这能跳过访问控制检查,实测可提升 30%~50% 调用速度 - 避免用
ConcurrentHashMap做简单缓存:如果 key 稳定(如固定几个 DTO 类),用静态 finalMap+ 双重检查更轻量
有没有比反射更快的替代方案?
有,但得看场景:
立即学习“Java免费学习笔记(深入)”;
- 频繁调用同一方法?用
java.lang.invoke.MethodHandle:它比Method.invoke()更接近 JVM 底层,支持 JIT 优化,实测调用开销降低 40%~60% - 需要动态代理?优先考虑
sun.misc.Unsafe(JDK 8/9)或VarHandle(JDK 9+)操作字段,绕过反射栈帧 - 纯字段读写场景?生成字节码(如 ByteBuddy)或使用 Lombok 的
@Wither/@Builder避免运行时反射 - 框架级通用逻辑(如 JSON 序列化)?直接用
Unsafe.defineAnonymousClass动态生成 accessor 类,这是 Jackson/Fastjson 真实采用的路子
别迷信“缓存万能”——如果某段代码每秒被调用上万次,哪怕缓存了 Method,invoke() 本身仍是瓶颈。这时候该换 MethodHandle 就换,该预生成字节码就预生成,反射只是手段,不是目的。










