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

时间:2014-05-29 10:46:40

标签: java performance 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%?

3 个答案:

答案 0 :(得分:29)

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

这是因为JIT编译器通过直接访问字段来嵌入getter方法,从而使它们成为相同的字节码。

答案 1 :(得分:12)

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

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

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

答案取决于VisualVM如何测量的方式:Before you start profiling, you need calibration data.这用于消除由分析器引起的开销时间。
虽然校准数据是正确的,但测量的最终计算不是。 VisualVM在字节码中看到方法调用。但它没有看到JIT编译器在优化时删除了这些调用 因此它消除了不存在的开销时间。这就是差异的出现方式。

答案 2 :(得分:0)

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

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

/**
    This method iterates through each character in a <code>String</code> and places each of them into a <code>java.util.List</code> of type <code>Character</code>.
    @param str The <code>String</code> to extract from.
    @param list The list to store each character into. (This is necessary because the compiler knows not which <code>List</code> to use, so it will automatically clear the list anyway.)
*/
public static void extractStringData(String str, List<Character> 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);
}

作为旁注,请注意反射会从您的程序中获得批次的性能。如果有必须访问的字段,方法或内部或嵌套类,无论如何都是非常不可能的,那么你应该使用反射。反射消除宝贵表现的主要原因是它抛出了相对无数的例外。我很高兴能帮到你!