class.getdeclaredmethods()返回的数组可缓存,但每次调用都新建数组对象,缓存仅省去元数据遍历和封装开销;类热替换时缓存会失效,须在类加载器稳定时使用,并建议用system.identityhashcode校验。

getDeclaredMethods 返回的数组是否可缓存
直接缓存 Class.getDeclaredMethods() 的返回值是安全的,但必须注意:该数组内容不可变,但数组对象本身每次调用都新建——JVM 不重用数组实例。所以缓存的是数组引用,不是避免重复反射扫描;真正省下的是类元数据遍历和方法封装开销。
常见错误是以为缓存了就能“一劳永逸”,其实如果类被热替换(如 Spring DevTools、JRebel),缓存的 Method[] 会指向已卸载类的残留句柄,后续调用抛 NoSuchMethodException 或 IllegalAccessError。
- 仅在类加载器生命周期稳定时缓存(如启动期单例初始化)
- 缓存前建议用
System.identityHashCode(clazz)做简单防伪校验(非强保障,但能捕获多数热更场景) - 不要缓存到静态 Map 中键为
Class对象——容易引发类加载器泄漏
循环中反复调用 Method.invoke 的性能瓶颈在哪
主要开销不在反射查找,而在每次 invoke() 的安全检查 + 参数包装 + 栈帧切换。尤其当目标方法是非 public 或含 varargs 时,JVM 需动态生成适配逻辑,开销陡增。
实测显示:对同一个 Method 对象连续调用 10 万次,开启 setAccessible(true) 后耗时可降 40%~60%;若方法签名含 Object...,再加一次自动装箱(如传 int),耗时翻倍不止。
- 务必在循环外调用
method.setAccessible(true)(且确认调用方有权限) - 避免在循环内做
method.invoke(obj, new Object[]{a, b})—— 每次都新建 Object 数组;改用预分配数组或基本类型拆包 - 若方法无参数,用
method.invoke(obj, (Object[])null)显式传 null,比空数组略快
为什么缓存 Method 对象比缓存 getDeclaredMethods 更值得优先做
getDeclaredMethods() 调用本身很快(纳秒级),但它的下游操作——比如遍历结果找某个特定方法名+签名——才是慢的源头。而 Method 对象一旦拿到,后续 invoke() 是唯一高频路径。
典型误操作:每次循环都 clazz.getDeclaredMethods() → 遍历匹配 → method.invoke()。这等于把 O(n) 查找塞进 O(m) 循环,整体变成 O(n×m)。
- 应提前在初始化阶段完成查找,缓存
Method引用(例如用Map<string method></string>,key 为"methodName#paramTypes") - 注意泛型擦除:
List<string></string>和List<integer></integer>在运行时都是List,不能单靠参数类名区分 - 若方法重载多,建议用
MethodType.methodType(returnType, paramTypes)(Java 7+)辅助校验签名一致性
Method 缓存失效的真实场景有哪些
最隐蔽的问题不是类卸载,而是 JDK 内部的 Method 实现优化:从 Java 8 开始,多次调用同一 Method.invoke() 可能触发 JVM 内联缓存(IC)或生成字节码桩(stub),但这些优化依赖方法句柄稳定性。一旦目标类被 redefine(如某些 AOP 代理、测试 Mock 工具),原有 Method 对象虽不抛异常,却可能调用到旧版本字节码。
另一个常被忽略的点:Lambda 表达式生成的方法(如 obj::getter)在 getDeclaredMethods() 中不可见,它们走的是 invokedynamic,无法通过反射缓存覆盖。
- 生产环境禁用
-XX:+UseSplitItables(旧版 JDK)或确认-XX:InlineSmallCode设置合理,避免 IC 失效放大波动 - 单元测试中用 Mockito mock 类时,其生成的 mock 类方法不会出现在原类的
getDeclaredMethods()结果里 - Android 上 Dalvik/ART 对反射缓存更敏感,
Method对象跨 ClassLoader 传递会直接失效
Method 对象确实能压平大部分波动,但真正的性能毛刺往往来自你没意识到的上下文变化——比如安全管理器开关、JIT 编译阈值跳变、甚至 GC 导致的元空间回收。别只盯着数组要不要缓存。











