反射调用比直接调用慢近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+ 双重检查更轻量
有没有比反射更快的替代方案?
有,但得看场景: