【问题标题】:java framework source has a pattern that assigns instance variable to local variablejava框架源有一个将实例变量分配给局部变量的模式
【发布时间】:2014-03-08 09:42:44
【问题描述】:

所以,java 框架中的pop()method java.util.Stack 类看起来像这样:

@SuppressWarnings("unchecked")
public synchronized E pop() {
    if (elementCount == 0) {
        throw new EmptyStackException();
    }
    final int index = --elementCount;
    final E obj = (E) elementData[index];
    elementData[index] = null;
    modCount++;
    return obj;
}

我难以理解的部分是局部变量index。看来我们不需要了。 elementCountVector 类中的一个实例变量,是 Stack 类扩展的。


所以我的意思是,

    final int index = --elementCount;
    final E obj = (E) elementData[index];
    elementData[index] = null;

这3行代码可以这样写

    final E obj = (E) elementData[--elementCount];
    elementData[elementCount] = null;

消耗更少的内存,因为index 局部变量的内存空间没有被使用。

另外,我在 java 框架源代码中发现了这种模式。例如java.util.ArrayList 类中的add(E Object) 方法看起来:

@Override public boolean add(E object) {
    Object[] a = array;
    int s = size;
    if (s == a.length) {
        Object[] newArray = new Object[s +
                (s < (MIN_CAPACITY_INCREMENT / 2) ?
                 MIN_CAPACITY_INCREMENT : s >> 1)];
        System.arraycopy(a, 0, newArray, 0, s);
        array = a = newArray;
    }
    a[s] = object;
    size = s + 1;
    modCount++;
    return true;
}

在本例中,array 是一个实例变量,如您所见,分配了一个新的局部变量 a 来保存它。

有人知道吗?非常感谢提前。 :)

【问题讨论】:

    标签: java variables frameworks instance local


    【解决方案1】:

    虽然这是一个很老的问题,但我想分享一些我在旅途中获得的信息。

    我可以在Performance Tips on Android 页面上找到关于我的问题的一些解释。先看页面中的示例代码,

    static class Foo {
        int mSplat;
    }
    
    Foo[] mArray = ...
    
    public void zero() {
        int sum = 0;
        for (int i = 0; i < mArray.length; ++i) {
            sum += mArray[i].mSplat;
        }
    }
    
    public void one() {
        int sum = 0;
        Foo[] localArray = mArray;
        int len = localArray.length;
    
        for (int i = 0; i < len; ++i) {
            sum += localArray[i].mSplat;
        }
    }
    
    public void two() {
        int sum = 0;
        for (Foo a : mArray) {
            sum += a.mSplat;
        }
    }
    

    根据上面的页面, zero() 最慢, one() 更快。因为它将所有内容都提取到局部变量中,避免了查找。

    我认为这个解释可能会解决我的第二个问题,即“分配了一个新的局部变量 a 来保存它。但是为什么?”

    我希望这可以帮助有同样好奇心的人。


    [EDIT]让我添加一些关于“lookups”的细节。

    因此,如果您编译上述代码并使用带有 -c 选项的 javap 命令反汇编类文件,它将打印出反汇编代码,即组成 Java 字节码的指令。

    public void zero();
    Code:
       0: iconst_0                          // Push int constant 0
       1: istore_1                          // Store into local variable 1 (sum=0)
       2: iconst_0                          // Push int constant 0
       3: istore_2                          // Store into local variable 2 (i=0)
       4: goto          22                  // First time through don't increment
       7: iload_1
       8: aload_0
       9: getfield      #14                 // Field mArray:[LTest$Foo;
      12: iload_2
      13: aaload
      14: getfield      #39                 // Field Test$Foo.mSplat:I
      17: iadd
      18: istore_1
      19: iinc          2, 1
      22: iload_2                           // Push value of local variable 2 (i)
      23: aload_0                           // Push local variable 0 (this)
      24: getfield      #14                 // Field mArray:[LTest$Foo;
      27: arraylength                       // Get length of array
      28: if_icmplt     7                   // Compare and loop if less than (i < mArray.length)
      31: return
    
    public void one();
    Code:
       0: iconst_0                          // Push int constant 0
       1: istore_1                          // Store into local variable 1 (sum=0)
       2: aload_0                           // Push this
       3: getfield      #14                 // Field mArray:[LTest$Foo;
       6: astore_2                          // Store reference into local variable (localArray)
       7: aload_2                           // Load reference from local variable
       8: arraylength                       // Get length of array
       9: istore_3                          // Store into local variable 3 (len = mArray.length)
      10: iconst_0                          // Push int constant 0
      11: istore        4                   // Store into local variable 4 (i=0)
      13: goto          29                  // First time through don't increment
      16: iload_1
      17: aload_2
      18: iload         4
      20: aaload
      21: getfield      #39                 // Field Test$Foo.mSplat:I
      24: iadd
      25: istore_1
      26: iinc          4, 1
      29: iload         4                   // Load i from local variable
      31: iload_3                           // Load len from local variable
      32: if_icmplt     16                  // // Compare and loop if less than (i < len)
      35: return
    

    这些指令有点陌生,所以我在JVM spec documents上查了一下。 (如果你很好奇,尤其是chapter 3, Compiling for the Java Virtual Machinechapter 6, The Java Virtual Machine Instruction Set 会很有帮助)。

    我添加了注释以帮助您理解,但简而言之,方法zero() 应该在每次迭代中运行getfield 指令。根据JVM spec documentation 3.8. Working with Class Instances 部分,getfield 操作执行如下几项工作。

    编译器生成对字段的符号引用 实例,它们存储在运行时常量池中。那些 运行时常量池项在运行时解析以确定 字段在被引用对象中的位置。

    【讨论】:

      【解决方案2】:

      These 3 lines of code can be written like

      我们的业务是制作有用且可扩展的程序,并且为了实现我们应该尽可能轻松地作为开发人员的生活。
      如果我需要多花 5 秒来阅读代码并且我可以简化它,我会的。特别是如果它以int 内存为代价.. 几乎不能称为优化。

      in this example, array is a instance variable, and as you can see, a new local variable a is assigned to hold it. Does anybody know about this?

      这几乎不是问题,我相信你的意思是这样说的:
      Why does they used another reference to array called a if they could use array ?

      好吧,我真的不明白为什么,因为他们本来可以使用 E 类型,因为它给了他们。这可能是协方差和逆变的原因,但我不确定。

      提示:另外,下次您添加语言源代码时,很高兴知道您正在查看哪个 JDK,并且提供一个链接对我很有帮助。

      【讨论】:

      • 感谢您的建议。您提供源代码链接以及我正在使用的 JDK 版本是正确的。
      • 我从 Android SDK 管理器获得了这个源代码,但找不到它是哪个 JDK 版本。另外,找不到java框架源代码的链接。
      【解决方案3】:

      请记住,--elementCount 在递减之前进行赋值。这意味着片段:

      final int index = --elementCount;
      final E obj = (E) elementData[index];
      elementData[index] = null;
      

      可以翻译成

      final int index = elementCount;
      elementCount--;
      final E obj = (E) elementData[index];
      elementData[index] = null;
      

      这意味着在您提议的替换“elementData[--elementCount]”和“elementData[elementCount]”中没有引用相同的项目。您建议的替换不等效。 希望这会有所帮助。

      【讨论】:

      • --elementCount 在递减后进行赋值,例如int a=1; int b=--a; System.out.println(b); 将打印 0。
      • @FlorentBayle 是对的。 --elementCount 在递减之后进行赋值。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-24
      • 1970-01-01
      • 2017-10-27
      • 2019-03-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多