我对一些代码进行了微基准测试(请保持良好状态)并遇到了这个难题:使用反射读取字段时,调用getter方法比读取字段更快。
简单测试类:
private static final class Foo {
public Foo(double val) {
this.val = val;
}
public double getVal() { return val; }
public final double val; // only public for demo purposes
}
我们有两个想法:
Method m = Foo.class.getDeclaredMethod("getVal", null);
Field f = Foo.class.getDeclaredField("val");
现在我在循环中调用两个反射,在方法上调用invoke
,在字段上调用get
。完成第一次运行以预热VM,第二次运行以10M次迭代完成。 方法调用始终快30%,但为什么?请注意,循环中调用了getDeclaredMethod和getDeclaredField 。它们被调用一次并在循环中的同一个对象上执行。
我还尝试了一些小的变化:使该字段非最终,传递,非公开等。所有这些组合导致统计上相似的性能。
编辑:这是在WinXP,Intel Core2 Duo,Sun JavaSE版本1.6.0_16-b01上运行,在jUnit4和Eclipse下运行。
答案 0 :(得分:3)
我有根据的猜测是如何实现getDeclaredField和getDeclaredMethod:每次调用它时,getDeclaredField都必须检查变量的类型和大小,以便返回实际的对象或基本类型,getDeclaredMethod会将指针返回到一个相同的方法,静态处理所有其余的方法。
修改强>:
我的解释类似:每个类只在内存中包含一次方法,而每个对象实例可以有不同的属性值。当您通过执行方法调用(仍然仅使用方法指针)获取属性值时,编译器已经优化了访问参数的方法,知道确切的类层次结构等,而当您通过使用“获取”获取属性的值时“,你让反射做getter方法的工作,显然没有编译器优化。
答案 1 :(得分:1)
在您的微基准测试中,方法调用更快,因为JVM / Hotspot在您的循环中进行了优化。
更改你的microbenchmak:
创建一个循环,其中:通过Reflection读取值,然后增加1(例如),然后通过Reflection分配给相同的Field。在循环之外,进行最后的读取和System.out.println吧......
执行两个变体(Field vs Method),你会发现真正的区别恰恰相反:方法调用实际上要慢30-40%。
此致
答案 2 :(得分:0)
这是否也意味着double d = Foo.getVal()
比double d = Foo.val
快30%?