JMH是Oracle官方推荐的微基准测试工具,能规避JIT预热、死码消除等干扰,准确量化Java反射字段访问性能;普通循环测量因JIT优化、未预热、GC干扰等导致结果失真。
Java反射访问字段的性能开销,不能靠“感觉”或简单
测量。JMH(Java Microbenchmark Harness)是 Oracle 官方推荐的微基准测试工具,能有效规避 JIT 预热、死码消除、指令重排序等干扰,给出可信的量化结果。
为什么普通循环测不准反射开销?
手写 for 循环 +
或
会严重低估或高估真实开销:
JIT 编译器可能将反射调用内联或完全优化掉(尤其在空循环中)
未预热时,第一次反射调用包含 Class 加载、Method/Field 解析、安全检查等一次性成本
未控制 GC 干扰,短时高频反射可能触发 Minor GC,把耗时“污染”到 GC 阶段
没有隔离 CPU 频率波动、上下文切换、TLB miss 等底层影响
用 JMH 正确对比字段访问方式
以读取一个 public int 字段为例,对比三种方式:
直接访问:
反射访问(已缓存 Field):
反射访问(每次重新 getDeclaredField):
关键配置要点:
立即学习
“
Java免费学习笔记(深入)
”;
Eclipse导入Android或其他的JAVA项目的正确方法 WORD版
本文档主要讲述的是Eclipse导入Android或其他的JAVA项目的正确方法;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
下载
使用
控制 JVM 参数
用
充分预热
用
多轮采样
将被测对象和 Field 声明为
成员,避免逃逸分析干扰
对反射调用,务必调用
,否则安全检查开销占比极高(可高出 3–5 倍)
典型结果与解读(JDK 17,Linux x64)
单位:ns/op(每操作纳秒数),数值越小越好:
直接访问:≈ 0.25 ns/op(基本等于一条 mov 指令)
缓存 + setAccessible 的反射:≈ 3.8 ns/op(约 15 倍于直接访问)
未缓存反射(每次查找字段):≈ 180 ns/op(超 700 倍,主要耗在字符串查找与安全检查)
注意:这些值随 JDK 版本持续优化。JDK 9+ 对
后的反射做了更多内联支持;JDK 16 起引入 VarHandle,性能接近直接访问,但 API 更严格。
生产环境建议
不为“反射慢”而拒绝用,而要控制它的使用边界:
字段/方法信息只解析一次,全局缓存
/
实例(如 Spring、MyBatis 均如此)
避免在高频循环体中重复调用
或
对极致性能敏感场景(如序列化框架),优先考虑
、
或运行时字节码生成(Byte Buddy / ASM)
用 JMH 定期回归验证——特别是升级 JDK 后,反射性能可能有意外提升或退化
System.nanoTime()System.currentTimeMillis()nanoTime()obj.valuefield.get(obj)clazz.getDeclaredField("value").get(obj)@Fork(jvmArgsAppend = {"-Xmx512m", "-XX:+UseG1GC"})@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)@Measurement(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)@State(Scope.Benchmark)setAccessible(true)setAccessible(true)FieldMethodgetDeclaredXXXgetMethodUnsafeVarHandle