【问题标题】:What is the purpose of using a local variable to hold a global one?使用局部变量来保存全局变量的目的是什么?
【发布时间】:2014-08-04 03:01:51
【问题描述】:

我查看了String.hashcode() 方法的源代码。这是6-b14 中的实现,已经改变了。

public int hashCode() {
        int h = hash;
        if (h == 0) {
            int off = offset;
            char val[] = value;
            int len = count;

            for (int i = 0; i < len; i++) {
                h = 31*h + val[off++];
            }
            hash = h;
        }
        return h;
}

我的问题是关于这一行的:

int len = count;

其中count 是一个全局变量,表示字符串的字符数。

为什么这里使用局部变量len 表示循环条件而不是全局变量本身?因为没有变量的操作,只有读取。如果全局字段用于读取或写入它,那么使用局部变量是否只是一种好习惯?如果答案是肯定的,为什么还要阅读?

【问题讨论】:

  • 您使用的是什么版本的 Java?
  • 这是6b-14。这是问题.. :)
  • s/global/instance/g
  • 也许在这种情况下纯粹是为了可读性? count 听起来模棱两可,len 几乎总是表示数组或字符串的长度。
  • @MDeSchaepmeester 对我来说,仅仅因为一个更好的名字就使用一个额外的变量听起来不对。

标签: java string global-variables local-variables


【解决方案1】:

在 String 类中四处寻找,我发现了一条关于 String.trim() 方法中对局部变量的奇怪赋值的注释,内容为 "avoid getfield opcode"

public String trim() {
    int len = value.length;
    int st = 0;
    char[] val = value;    /* avoid getfield opcode */

    while ((st < len) && (val[st] <= ' ')) {
        st++;
    }
    while ((st < len) && (val[len - 1] <= ' ')) {
        len--;
    }
    return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}

所以正如Frank Olschewski 所指出的那样,整个事情似乎都与性能有关。

在 Java 字节码中,实例变量实际上是由对象和名称引用的(使用 GETFIELD 指令)。如果没有优化,VM 必须做更多的工作才能访问该变量。

因此,代码的潜在性能损失是它在每次循环时使用相对昂贵的GETFIELD 指令。该方法中的本地分配消除了每次循环时都需要GETFIELD

JIT 优化器可能会优化循环,但也可能不会,因此开发人员可能采取了手动执行它的安全路径。

Avoiding getfield opcode 上有一个单独的问题,其中包含详细信息。

【讨论】:

  • 感谢您挖掘源评论和链接。
【解决方案2】:

如果count 可以修改,那么您需要一个局部变量。如果您正在进行多线程,那么您需要一个局部变量。创建局部变量是最安全的。但是,这并不是绝对必要的。

在这种情况下,这是多余的,因为字符串无论如何都是不可变的。 count 的值甚至不能改变。

它几乎没用,这就是为什么在 Java 8 中它看起来像这样:

public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

他们甚至不再有计数,他们正在使用value.length,其中value 是最终的字符数组。

他们正在做char val[] = value,但这只是一个参考,完全没有必要。

使用局部变量可能会进行一些细微的微增强,或者可能是为了可读性而进行的,但这不是必需的(在我看来可读性较差)。

【讨论】:

  • 但是如果没有同步,局部变量在多线程中无论如何都是无用的。
  • @SteveBennett 我同意,它没用,这就是他们摆脱它的原因。
  • 那么问题来了,他们为什么会有char val[] = value
  • @JohnFarrelly 这完全没有必要。无论如何,这只是一个参考......o.0
【解决方案3】:

访问局部变量比访问实例变量更快。新的 Java 8 代码(参见 Anubians 的回答)也考虑到了这一点。这就是为什么他们使用局部变量h 进行哈希计算而不直接访问实例变量this.hash 并创建局部指针char val[] = value; 的原因。但考虑到这一点,我不知道他们为什么不在 for 循环中使用 i &lt; val.length; 甚至更好的 z = val.length; i &lt; z;,而是使用 i &lt; value.length;

【讨论】:

  • “访问局部变量比访问实例变量更快” 真的吗?你怎么知道的?
  • 性能是个好点,但正如你提到的,实现似乎并不是真正的点。
  • 性能优化是对的。我选择了 MicSim 的答案,因为它更完整。
猜你喜欢
  • 1970-01-01
  • 2017-12-30
  • 1970-01-01
  • 1970-01-01
  • 2016-01-17
  • 2019-07-24
  • 2017-11-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多