Java:Getter和setter比直接访问更快?

我测试了我在Linux上网本上使用VisualVM 1.3.7编写的Java光线跟踪器的性能。 我用剖析器测量过。
为了好玩,我测试了使用getter和setter以及直接访问字段之间的区别。 getter和setter是标准代码,没有添加。

我没想到会有任何分歧。 但是直接访问代码的速度较慢。

这是我在Vector3D中测试的样本:

public float dot(Vector3D other) { return x * other.x + y * other.y + z * other.z; } 

时间:1542毫秒/ 1,000,000次调用

 public float dot(Vector3D other) { return getX() * other.getX() + getY() * other.getY() + getZ() * other.getZ(); } 

时间:1453毫秒/ 1,000,000次调用

我没有在微基准测试中测试它,而是在光线跟踪器中测试它。 我测试代码的方式:

  • 我用第一个代码启动了程序并进行了设置。 光线跟踪器尚未运行。
  • 我启动了分析器并在初始化完成后等了一会儿。
  • 我开始了一个光线追踪器。
  • 当VisualVM显示足够的调用时,我停止了探查器并等了一会儿。
  • 我关闭了光线追踪程序。
  • 我用第二个代码替换了第二个代码,并在编译后重复上述步骤。

我至少为这两个代码运行了20,000,000次调用。 我关闭了任何我不需要的程序。 我设置CPU的性能,所以我的CPU时钟是最大的。 每时每刻。
第二个代码如何快6%?

我做了一些微型基准测试,大量的JVM热身,发现这两种方法的执行时间完全相同

发生这种情况是因为JIT编译器在getter方法中嵌入了对字段的直接访问,从而使它们成为相同的字节码。

谢谢大家帮我回答这个问题。 最后,我找到了答案。

首先, 波希米亚是正确的 :使用PrintAssembly我检查了生成的汇编代码是相同的假设。 是的,虽然字节码不同,但生成的代码是相同的。
所以masterxilo是对的 :探查器必须是罪魁祸首。 但是masterxilo对时序栅栏和更多仪器代码的猜测并不属实; 两个代码最终都是相同的。

所以还有一个问题:第二个代码在Profiler中的速度有多快6%?

答案取决于VisualVM如何测量: 在开始分析之前,您需要校准数据。 这用于消除分析器导致的开销时间。
虽然校准数据是正确的,但测量的最终计算不是。 VisualVM在字节码中看到方法调用。 但它没有看到JIT编译器在优化时删除了这些调用。
因此它消除了不存在的开销时间。 这就是差异的出现方式。

如果您没有参加统计学课程,无论编写的程度如何,程序性能总会存在差异。 这两种方法似乎以大致相同的速率运行的原因是因为访问器字段只做一件事:它们返回一个特定的字段。 因为在存取方法中没有其他任何事情发生,所以这两种策略几乎都做同样的事情; 但是,如果您不了解封装,即程序员隐藏用户数据(字段或属性)的程度,封装的主要规则就是不向用户显示内部数据 。 将字段修改为公共意味着任何其他类都可以访问这些字段,这对用户来说可能非常危险。 这就是为什么我总是建议Java程序员使用访问器和mutator方法,这样字段就不会落入坏人之手。

如果您对如何访问私有字段感到好奇,可以使用reflection,实际访问特定类的数据,以便在必须这样做时可以改变它。 作为一个无聊的例子,假设您知道java.lang.String类包含char []类型的私有字段(即char数组)。 它对用户隐藏,因此您无法直接访问该字段。 (顺便说一句,方法java.lang.String.toCharArray()为你访问该字段。)如果你想连续访问每个字符并将每个字符存储到一个集合中(为了简单起见,为什么不是java。 util.List?),这里是如何在这种情况下使用reflection:

 /** This method iterates through each character in a String and places each of them into a java.util.List of type Character. @param str The String to extract from. @param list The list to store each character into. (This is necessary because the compiler knows not which List to use, so it will automatically clear the list anyway.) */ public static void extractStringData(String str, List list) throws IllegalAccessException, NoSuchFieldException { java.lang.reflect.Field value = String.class.getDeclaredField("value"); value.setAccessible(true); char[] data = (char[]) value.get(str); for(char ch : data) list.add(ch); } 

作为旁注,请注意reflection会从您的程序中获得很多性能。 如果有一个字段,方法,内部或嵌套类,你必须以任何理由访问它(这种情况极不可能),那么你应该使用reflection。 reflection消除宝贵表现的主要原因是它抛出了相对无数的例外。 我很高兴能帮到你!