Java 反射机制(Method.invoke 调用链)
Java 反射机制(Method.invoke 调用链)技术文档
0. 定位声明
适用版本:JDK 8 / JDK 11 / JDK 17 / JDK 21(关键差异点会逐一标注)
前置知识:需理解 JVM 类加载机制、Java 内存模型(JMM)、基础 OOP 概念、AccessController 安全模型
不适用范围:
- 不覆盖 MethodHandle(JSR 292)和 VarHandle,二者与反射有本质区别
- 不覆盖 GraalVM Native Image 下的反射限制(需额外配置)
- 不覆盖 Kotlin/Scala 对 Java 反射的封装层
1. 一句话本质
用最简单的话说:
反射就像一本"说明书"。你手里有一个零件(对象),但你不知道怎么用它。反射让你在程序运行时,翻开这本说明书,找到零件的按钮(方法),然后按下去——哪怕你写代码时根本不知道这个零件长什么样。
更精确地说:Method.invoke 是 Java 反射体系的核心执行引擎,它允许在运行时(而不是编译时)动态地根据方法名、参数类型找到并调用任意对象上的任意方法,绕过了编译期的类型绑定约束。
2. 背景与根本矛盾
历史背景
Java 1.1(1997年)引入反射 API,核心驱动力来自两个工程需求:
- IDE 工具链需求:IDE 需要在编译期之外探知类的结构(字段、方法、注解),以提供代码补全、类型检查。
- 框架即插即用需求:Spring、Hibernate 等框架需要在不修改用户代码的前提下,动态注入依赖、拦截方法——“我不知道你会传来哪个类,但我能动态调用它的方法”。
JDK 8 之前,Method.invoke 的底层实现经历了从纯 JNI(Native)到 Java 字节码生成(MethodAccessor)的演化。JDK 9 引入模块系统后,反射访问权限被进一步收紧,--add-opens 成为常见启动参数。
根本矛盾(Trade-off)
| 对立约束 | 说明 |
|---|---|
| 灵活性 vs 性能 | 动态调用无法被 JIT 内联优化(至少在 inflation 阈值前),比直接调用慢 10x~100x |
| 开放性 vs 安全性 | 反射可绕过 private 访问修饰符,破坏封装;JDK 9+ 模块系统开始对此收紧 |
| 通用性 vs 类型安全 | 反射调用在编译期不做类型检查,所有错误推迟到运行时 InvocationTargetException |
3. 核心概念与领域模型
3.1 关键术语表
| 术语 | 费曼式定义 | 正式定义 |
|---|---|---|
Class<?> |
每个类在 JVM 里有一张"身份证",Class 对象就是这张证 |
JVM 中代表一个类的元数据对象,每个类只有一个 Class 实例(由类加载器保证) |
Method |
从"身份证"上摘下来的某一个方法的描述信息,包括名字、入参、返回值 | java.lang.reflect.Method,表示某个类的一个方法的元信息对象 |
MethodAccessor |
真正执行方法调用的"执行器",有本地(Native)和Java字节码两种 | Method.invoke 内部委托的接口,由 sun.reflect.ReflectionFactory 创建 |
| Inflation 机制 | 第1次用榔头(Native),用够15次换成电钻(字节码生成),更高效 | JVM 在第 inflationThreshold(默认15)次反射调用后,从 NativeMethodAccessor 切换到 GeneratedMethodAccessor |
AccessCheck |
调用前的"门卫检查",看你有没有权限进入这个方法 | 基于 Reflection.quickCheckMemberAccess 和 checkAccess 的权限校验流程 |
3.2 领域模型
┌─────────────────────────────┐
│ Class<T> 对象 │ ← 每个类只有一个,由 ClassLoader 管理
│ (java.lang.Class) │
└────────────┬────────────────┘
│ getDeclaredMethod(name, paramTypes)
▼
┌─────────────────────────────┐
│ Method 对象 │ ← 每次 getDeclaredMethod 返回新对象,但共享底层 root
│ (java.lang.reflect.Method) │
│ - name │
│ - parameterTypes[] │
│ - returnType │
│ - override (accessible flag) │
└────────────┬────────────────┘
│ invoke(obj, args)
▼
┌─────────────────────────────┐
│ MethodAccessor 接口 │
└───┬───────────────────┬──────┘
│ │
┌────────────▼──┐ ┌──────────▼──────────────┐
│ NativeMethod │ │ GeneratedMethodAccessor │
│ Accessor │ │ (动态字节码生成) │
│ (JNI 实现) │ │ 直接调用目标方法,无反射开销 │
└───────────────┘ └──────────────────────────┘
调用次数 < 15 时使用 调用次数 ≥ 15 次后切换(Inflation)
4. 对比与选型决策
4.1 同类动态调用方式横向对比
| 方式 | 代码复杂度 | 首次调用延迟 | 热点调用性能 | 类型安全 | JDK 要求 |
|---|---|---|---|---|---|
Method.invoke |
低 | ~微秒级 | 比直接调用慢 2x~5x(Inflation后) | 运行时检查 | JDK 1.1+ |
MethodHandle (LambdaMetafactory) |
中 | 较高(首次生成字节码) | 接近直接调用,可被JIT内联 | 编译期部分检查 | JDK 7+ |
cglib/ByteBuddy 代理 |
高 | 高(需生成代理类) | 接近直接调用 | 运行时 | JDK 8+ |
| 直接调用 | 无 | 最低 | 最优,JIT完全优化 | 编译期 | - |
⚠️ 注意:上述性能数据基于 JMH 基准测试的典型值,实际受 JIT 编译状态、方法参数数量、GC 压力等因素影响,建议在目标环境实测。
4.2 选型决策树
需要动态调用方法?
├── 调用频率低(< 1000次/秒),框架初始化阶段?
│ └── ✅ 直接用 Method.invoke,简单够用
├── 调用频率高,在热路径上(如 RPC 序列化、ORM 映射)?
│ ├── JDK 7+?→ ✅ 用 MethodHandle + LambdaMetafactory,可被JIT内联
│ └── 需要更强控制(拦截器、AOP)?→ ✅ 用 ByteBuddy/cglib 生成子类
└── 需要访问 private 成员?
├── JDK 8 及以前?→ setAccessible(true) 即可
└── JDK 9+?→ 需要 --add-opens 或模块声明 opens
5. 工作原理与实现机制
5.1 静态结构:核心类与数据结构
java.lang.reflect.Method
├── root: Method // 指向"根"Method对象(共享底层信息)
├── methodAccessor: MethodAccessor // 懒初始化,真正执行调用的委托
├── override: boolean // setAccessible(true) 后为 true,跳过访问检查
└── clazz: Class<?> // 声明该方法的类
为什么 Method 有 root 概念?
每次调用 getDeclaredMethod 都会返回一个新的 Method 对象,但底层的 MethodAccessor 很重,不能每次都创建。JDK 的做法是:所有同名同参数的 Method 对象共享同一个 root(第一次创建的那个),root 上持有 MethodAccessor,拷贝对象委托给它。
5.2 动态行为:Method.invoke 完整调用链
以 method.invoke(obj, args) 为例,逐步拆解:
第 1 步:Method.invoke(obj, args)
│
├─ 1.1 检查 override 标志
│ if (!override) → 执行 checkAccess()
│ - 获取调用者 Class(通过 Reflection.getCallerClass())
│ - 比对调用者与方法所在类的访问权限
│ - 失败 → 抛出 IllegalAccessException
│
├─ 1.2 获取或创建 MethodAccessor
│ if (methodAccessor == null) → acquireMethodAccessor()
│ - 查找 root.methodAccessor
│ - 若 root 也没有 → ReflectionFactory.newMethodAccessor(method)
│ → 创建 DelegatingMethodAccessor
│ └─ 包装 NativeMethodAccessor(初始实现)
│
└─ 1.3 委托给 MethodAccessor.invoke(obj, args)
第 2 步:DelegatingMethodAccessor.invoke(obj, args)
│
└─ 委托给内部持有的 accessor(初始是 NativeMethodAccessor)
第 3 步:NativeMethodAccessor.invoke(obj, args)
│
├─ 3.1 numInvocations 计数器 +1
│
├─ 3.2 判断是否达到 inflationThreshold(默认 15)
│ if (numInvocations > inflationThreshold && !ReflectionFactory.noInflation)
│ → 生成 GeneratedMethodAccessorXXX(ASM 字节码)
│ → 将 DelegatingMethodAccessor 内部指针切换到新 accessor
│
└─ 3.3 调用 invoke0(method, obj, args) ← JNI native 方法
第 4 步(Inflation 后):GeneratedMethodAccessorXXX.invoke(obj, args)
│
└─ 直接调用目标方法(等价于直接字节码调用,无 JNI 开销)
如:((TargetClass)obj).targetMethod(args[0], args[1])
5.3 关键设计决策
决策1:为什么要有 Inflation 机制(Native → 字节码生成)?
Native 方法调用(JNI)有固定的 per-call 开销(~100-500ns),但无需额外的类加载时间。字节码生成的 Accessor 首次创建需要 ASM 生成并加载新类(~几十ms 级别),但热路径可被 JIT 编译、内联,每次调用开销可降至 ~10ns 级别。
Trade-off:如果每个 Method 都立即生成字节码 Accessor,低频调用反而因类加载开销付出额外代价。设定阈值(默认15次)是统计意义上的"大多数低频调用不值得生成"的经验值。
决策2:为什么 setAccessible(true) 能绕过访问控制?
Java 访问控制(public/private)是编译器和反射 API 的约定,而不是 JVM 字节码层面的强制约束。JVM 层面所有方法都是"可调用的"。
setAccessible(true)只是把override标志置为 true,让 invoke 方法跳过checkAccess()那一步。这是 Java 反射故意留的"后门",供框架使用。JDK 9 模块系统出现后,
setAccessible在跨模块访问时受到额外限制,需要模块显式声明opens,这是对该后门的补救。
决策3:为什么 getDeclaredMethod 每次返回新对象但共享 MethodAccessor?
如果共享 Method 对象,多线程同时调用
setAccessible(true)会互相干扰(一个线程开启访问权,影响到另一个线程的安全约束)。返回新对象保证了accessible标志的隔离。但MethodAccessor(执行逻辑)共享,可避免重复的字节码生成开销(字节码生成是线程安全且幂等的)。
6. 高可靠性保障
6.1 异常体系
反射调用的异常需要特别理解:
invoke() 可抛出的异常:
├── IllegalAccessException ← 调用者无权访问该方法(未 setAccessible)
├── IllegalArgumentException ← 参数类型不匹配 或 obj 类型错误
├── InvocationTargetException ← 目标方法自身抛出了异常(包装在 cause 里)
└── NullPointerException ← obj 为 null 且方法是实例方法
生产中最常见的陷阱:
框架代码调用 invoke 后,必须 catch(InvocationTargetException e) 并 unwrap:
try {
method.invoke(obj, args);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause(); // 取出真正的业务异常
if (cause instanceof RuntimeException) throw (RuntimeException) cause;
throw new RuntimeException(cause);
}
6.2 可观测性:关键监控指标
| 指标 | 获取方式 | 正常阈值 | 告警阈值 |
|---|---|---|---|
| 反射调用耗时 | JMH / Arthas monitor |
< 1μs(Inflation后) | > 100μs 持续 |
MethodAccessor 生成次数 |
JVM GC 日志(观察短命类) | 启动期集中,运行期接近0 | 运行期持续增长(内存泄漏风险) |
| Metaspace 使用量 | jstat -gc / JMX |
视应用规模,通常 < 256MB | 持续增长未收敛 |
| 反射相关类数量 | jmap -histo 过滤 GeneratedMethodAccessor |
启动后稳定 | 持续增长 |
7. 使用实践与故障手册
7.1 典型生产级使用示例
环境:JDK 17,Spring Framework 6.x 场景
// 示例:框架级反射工具方法(生产可用)
// 运行环境:JDK 11+
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ConcurrentHashMap;
public class ReflectionUtils {
// ✅ 生产关键点1:缓存 Method 对象,避免每次 getDeclaredMethod 的查找开销(~1μs/次)
private static final ConcurrentHashMap<String, Method> METHOD_CACHE = new ConcurrentHashMap<>();
public static Object invokeMethod(Object target, String methodName,
Class<?>[] paramTypes, Object[] args) {
String cacheKey = target.getClass().getName() + "#" + methodName;
Method method = METHOD_CACHE.computeIfAbsent(cacheKey, k -> {
try {
Method m = target.getClass().getDeclaredMethod(methodName, paramTypes);
m.setAccessible(true); // ✅ 生产关键点2:提前 setAccessible,避免每次 invoke 时的权限检查
return m;
} catch (NoSuchMethodException e) {
throw new RuntimeException("Method not found: " + k, e);
}
});
try {
return method.invoke(target, args);
} catch (InvocationTargetException e) {
// ✅ 生产关键点3:必须 unwrap InvocationTargetException,否则调用栈被包裹,难以排查
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
throw new RuntimeException("Reflective call failed", cause);
} catch (IllegalAccessException e) {
throw new RuntimeException("Illegal access to method: " + methodName, e);
}
}
}
JDK 9+ 模块系统下的额外配置(JVM 启动参数):
# 若框架需要访问 JDK 内部类(如 Spring 访问 java.lang 私有字段)
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
# 关闭 Inflation,强制所有反射调用直接使用字节码生成(适合长期运行的高频调用场景)
-Dsun.reflect.noInflation=true
# 修改 Inflation 阈值(默认15,可调低到1以加速热路径初始化)
-Dsun.reflect.inflationThreshold=5
7.2 故障模式手册
【故障1:IllegalAccessException: module java.base does not open xxx to unnamed module】
- 现象:JDK 9+ 升级后,原有框架代码启动报错
- 根本原因:JDK 9 模块系统对反射访问进行了封装限制,默认不允许跨模块访问私有成员
- 预防措施:升级前检查所有 setAccessible(true) 的使用点,确认是否跨模块
- 应急处理:JVM 启动参数添加 --add-opens {module}/{package}=ALL-UNNAMED
【故障2:Metaspace OOM(GeneratedMethodAccessor 类泄漏)】
- 现象:应用运行一段时间后 Metaspace 持续增长,jmap 发现大量 GeneratedMethodAccessorXXX 类
- 根本原因:每次对新的 Method 对象(未缓存)触发 Inflation,都会生成并加载一个新类;
若类加载器频繁创建销毁,这些类无法被 GC
- 预防措施:
1. 强制缓存 Method 对象(参见 7.1 示例)
2. 动态代理场景使用固定 ClassLoader
- 应急处理:重启;长期修复需排查 Method 对象是否被正确缓存
【故障3:InvocationTargetException 包裹真实异常,日志难以排查】
- 现象:日志中只看到 InvocationTargetException,真实业务异常被埋藏在 cause 链深处
- 根本原因:Method.invoke 将目标方法的所有异常统一包装为 InvocationTargetException
- 预防措施:所有 invoke 调用点必须显式 unwrap(见 7.1 示例)
- 应急处理:通过 Arthas 的 watch 命令实时观察真实异常:
watch com.example.YourClass yourMethod "{params, returnObj, throwExp}" -x 2
【故障4:反射调用性能劣化,CPU 飙高】
- 现象:热路径中反射调用成为 CPU 热点(Arthas flame graph 可见)
- 根本原因:
1. 未缓存 Method 对象,每次 getDeclaredMethod 触发方法查找(O(n) 遍历)
2. Inflation 阈值未到,持续使用 NativeMethodAccessor
3. setAccessible 未提前调用,每次 invoke 触发 AccessCheck
- 预防措施:缓存 Method + 提前 setAccessible + 考虑迁移到 MethodHandle
- 应急处理:Arthas monitor 定位调用频率,评估是否需要替换为 LambdaMetafactory
7.3 边界条件与局限性
- 方法参数为基本类型时:args 数组中需传包装类(自动装箱),JVM 会在 NativeMethodAccessor 层面拆箱;频繁调用时装箱/拆箱本身带来额外 GC 压力。
- 静态方法调用:
method.invoke(null, args),obj参数传 null,忽略即可;但若误传非 null 也不会报错(JVM 忽略)。 - 数组参数:
invoke(obj, new Object[]{array})与invoke(obj, (Object)array)语义不同,后者会被展开为 varargs,可能导致IllegalArgumentException。 - JIT 内联限制:即便 Inflation 后,
GeneratedMethodAccessor在某些 JIT 场景下仍无法被完全内联(取决于调用点多态性),性能上限低于MethodHandle(⚠️ 存疑:具体 JIT 行为依赖 JVM 版本和参数)。
8. 性能调优指南
8.1 性能瓶颈识别
反射调用性能问题定位路径:
1. 火焰图 / Arthas profiler → 确认反射调用是否在热路径
arthas: profiler start --event cpu --duration 30
2. 检查 getDeclaredMethod 是否被频繁调用(未缓存)
arthas: monitor com.example.ReflectionUtils getDeclaredMethod -c 5
3. 检查 Inflation 是否完成(是否还在使用 NativeMethodAccessor)
arthas: watch sun.reflect.NativeMethodAccessorImpl invoke 'returnObj' -n 100
4. 检查 setAccessible 是否提前调用(避免每次 invoke 做 AccessCheck)
8.2 调优步骤(按优先级)
P0 - 缓存 Method 对象
调优目标:getDeclaredMethod 调用次数降为 0(除初始化阶段)
验证方法:Arthas monitor getDeclaredMethod,热稳态下调用次数为 0
预期收益:消除每次方法查找的 O(n) 遍历开销,约 1-5μs/次
P1 - 提前 setAccessible(true)
调优目标:invoke 内部 AccessCheck 耗时降为 0
验证方法:JMH 对比 setAccessible 前后 invoke 耗时
预期收益:约 0.1-0.5μs/次(视安全管理器配置)
P2 - 调整 inflationThreshold
调优目标:缩短从 Native 切换到字节码生成 Accessor 的时间
推荐值:-Dsun.reflect.inflationThreshold=5(对于确定高频的调用点)
验证方法:JMH 测量调用链稳定后的 QPS
风险:会导致更多一次性 Accessor 类生成(对低频 Method 造成浪费)
P3 - 迁移到 LambdaMetafactory(高频极致场景)
// JDK 8+,可将反射调用转为函数式接口,速度接近直接调用
// 运行环境:JDK 8+
import java.lang.invoke.*;
import java.util.function.Function;
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType methodType = MethodType.methodType(String.class); // 目标方法签名
MethodHandle handle = lookup.findVirtual(MyClass.class, "getName", methodType);
// 生成函数式接口,之后调用完全绕过反射,可被JIT内联
CallSite site = LambdaMetafactory.metafactory(
lookup,
"apply",
MethodType.methodType(Function.class),
MethodType.methodType(Object.class, Object.class),
handle,
MethodType.methodType(String.class, MyClass.class)
);
Function<MyClass, String> getter = (Function<MyClass, String>) site.getTarget().invokeExact();
// 之后:getter.apply(obj) 速度接近直接调用
8.3 调优参数速查表
| 参数 | 默认值 | 推荐值 | 调整风险 |
|---|---|---|---|
sun.reflect.inflationThreshold |
15 | 5(高频场景)/ 保持默认(低频场景) | 过低导致 Metaspace 压力 |
sun.reflect.noInflation |
false | true(极端高频且 Method 已稳定) | 所有反射立即生成字节码,启动变慢 |
--add-opens |
无 | 按需添加,不要用通配符 | 过度开放破坏模块封装 |
9. 演进方向与未来趋势
9.1 Project Valhalla(值类型)对反射的影响
JDK 21 LTS 引入的 value class 和未来 Valhalla 的 Primitive Class,其实例不再有对象头(object header),反射调用时的参数传递语义会发生变化。框架依赖反射修改字段的场景(如 Hibernate 字段注入)需要重新适配。
9.2 JEP 416(Method.invoke 重构,JDK 18+)
JDK 18 的 JEP 416 将 Method.invoke 的实现基础从 sun.reflect.MethodAccessor 迁移到 java.lang.invoke.MethodHandle,使反射调用可以直接受益于 MethodHandle 的 JIT 优化链路。
对使用者的实际影响:
- JDK 18+ 中
Method.invoke的性能上限提升,Inflation 机制可能逐步淡化 - 现有代码无需修改,性能自动受益
- 但 JDK 9-17 的大量生产环境仍需遵循本文的传统优化路径
10. 面试高频题
【基础理解层】(考察概念掌握)
Q:反射是什么?Method.invoke 和直接方法调用有什么区别?
A:反射是 JVM 提供的在运行时获取类信息并动态调用方法的能力。
直接调用在编译时确定调用目标,JIT 可完全内联优化;
Method.invoke 在运行时通过方法名查找目标方法,增加了查找和访问控制检查的开销,
且在 Inflation 完成前通过 JNI 调用,性能低于直接调用。
考察意图:区分候选人是否理解"编译期绑定 vs 运行时绑定"的本质差异。
【原理深挖层】(考察内部机制理解)
Q:什么是反射的 Inflation 机制?为什么要设计这个机制?
A:Inflation 是 JVM 对反射调用的性能优化策略:
前 N 次(默认15次)使用 JNI 的 NativeMethodAccessor 执行,无需额外类加载;
N 次后,JVM 用 ASM 动态生成一个 GeneratedMethodAccessorXXX 字节码类,
该类直接调用目标方法,可被 JIT 编译内联,消除 JNI 开销。
设计原因:一次性调用不值得承担字节码生成(类加载)的固定开销;
高频调用则需要字节码生成来消除持续的 JNI overhead。
这是"启动开销 vs 运行时性能"的经典权衡。
考察意图:验证候选人是否真正阅读过 JDK 源码或理解 JVM 动态优化机制。
【原理深挖层】
Q:getDeclaredMethod 每次都返回新对象,但 Method.invoke 性能为何不会每次都很差?
A:Method 对象内部有 root 引用机制。每个 Method 的拷贝对象都持有指向原始 root 的引用,
MethodAccessor(包含生成的字节码执行器)存储在 root 上并被所有拷贝共享。
因此,即使每次 getDeclaredMethod 返回新 Method 对象,
Inflation 后生成的 GeneratedMethodAccessor 只需创建一次。
但这不意味着可以随意调用 getDeclaredMethod:方法查找本身(遍历方法表)仍有开销。
考察意图:考察对 JDK 源码细节的掌握程度,区分"Method 对象"和"MethodAccessor"。
【生产实战层】(考察工程经验)
Q:你在生产中遇到过反射相关的性能或稳定性问题吗?如何排查和解决?
A(参考回答框架):
1. 常见问题:Metaspace OOM(未缓存 Method 导致 Accessor 类泄漏)、
JDK 9 升级后 IllegalAccessException(模块系统限制)、
InvocationTargetException 包裹异常导致排查困难
2. 排查工具:Arthas(watch/monitor/profiler)、jmap -histo、火焰图
3. 解决方案:缓存 Method 对象 + 提前 setAccessible + InvocationTargetException unwrap
4. 进阶:高频路径迁移 LambdaMetafactory
考察意图:区分背书本和有实际工程经验的候选人。
【生产实战层】
Q:Spring 大量使用反射,为什么 Spring 应用的性能依然可以接受?
A:Spring 在以下维度对反射开销做了缓解:
1. 反射调用主要集中在启动阶段(Bean 初始化、依赖注入),运行期直接调用代理对象
2. Spring 使用 cglib/CGLIB 动态代理,运行期方法拦截通过字节码子类直接调用,非反射
3. Spring 内部缓存了大量 Method/Field 对象(ReflectionUtils 内置缓存)
4. JDK 18+ 的 Method.invoke 重构进一步缩小了差距
考察意图:考察候选人对主流框架实现原理的理解深度。
11. 文档元信息
验证声明
本文档内容经过以下验证:
✅ 与官方文档一致性核查:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/reflect/Method.html
✅ Inflation 机制基于 JDK 源码分析:sun.reflect.NativeMethodAccessorImpl(OpenJDK 17)
✅ JEP 416 信息核查:https://openjdk.org/jeps/416
⚠️ 以下内容未经本地环境验证,仅基于文档推断:
- 第8.2节 P3 LambdaMetafactory 示例代码(需在 JDK 17 环境实测)
- 第6.2节 具体性能数值(受 JVM 版本、硬件环境影响,建议自行 JMH 验证)
- 第9节 Valhalla 对反射影响(基于 JEP Preview 状态,可能变化)
知识边界声明
本文档适用范围:JDK 8 ~ JDK 21,Linux / macOS x86_64 / ARM64 环境
不适用场景:
- GraalVM Native Image(反射需要 reflect-config.json 额外配置,行为有差异)
- Android(使用 ART 虚拟机,反射实现与 HotSpot 有差异)
- Kotlin 反射(kotlin-reflect 库是对 Java 反射的高层封装,行为有差异)
参考资料
官方文档:
- Java SE 17 反射 API:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/reflect/package-summary.html
- JEP 416(Method.invoke 重构):https://openjdk.org/jeps/416
- Java 模块系统(JPMS):https://openjdk.org/projects/jigsaw/
核心源码(OpenJDK 17):
- java.lang.reflect.Method:https://github.com/openjdk/jdk17/blob/master/src/java.base/share/classes/java/lang/reflect/Method.java
- sun.reflect.NativeMethodAccessorImpl:https://github.com/openjdk/jdk17/blob/master/src/java.base/share/classes/sun/reflect/NativeMethodAccessorImpl.java
延伸阅读:
- 《深入理解Java虚拟机》第3版 第9章(类加载机制)
- Arthas 官方文档(反射问题排查):https://arthas.aliyun.com/doc/
- JMH 官方文档(性能基准测试):https://openjdk.org/projects/code-tools/jmh/
- JEP 181(嵌套访问控制):https://openjdk.org/jeps/181
更多推荐




所有评论(0)