Java 循环变量的作用域

Java 循环变量的作用域,java,while-loop,garbage-collection,scope,Java,While Loop,Garbage Collection,Scope,今天,我们在工作中讨论了以下两个函数中哪一个性能更好,内存消耗更少,并且不强调gc public class Main { public void do1() { int result = 0; int val; do { val = calc(result); result = val * 2; } while (result < 1000); }

今天,我们在工作中讨论了以下两个函数中哪一个性能更好,内存消耗更少,并且不强调gc

public class Main {

    public void do1() {
        int result = 0;
        int val;
        do {
            val = calc(result);
            result = val * 2;
        } while (result < 1000);
    }

    public void do2() {
        int result = 0;
        do {
            final int val = calc(result);
            result = val * 2;
        } while (result < 1000);
    }

    private int calc(final int i) {
        return i + 2;
    }
}
公共类主{
公共无效do1(){
int结果=0;
int-val;
做{
val=计算值(结果);
结果=val*2;
}结果<1000;
}
公开无效do2(){
int结果=0;
做{
最终int val=计算(结果);
结果=val*2;
}结果<1000;
}
私人整数计算(最终整数i){
返回i+2;
}
}
在我看来,两者都是等价的,无论val是在循环内部还是外部定义的。我唯一想到的是变量的范围应该尽可能地缩小。但是,在讨论gc如何处理这些情况时,我们没有找到解决方案

我们看了字节码,没有区别

Compiled from "Main.java"
public class Main {
  public Main();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void do1();
    Code:
       0: iconst_0
       1: istore_1
       2: aload_0
       3: iload_1
       4: invokespecial #2                  // Method calc:(I)I
       7: istore_2
       8: iload_2
       9: iconst_2
      10: imul
      11: istore_1
      12: iload_1
      13: sipush        1000
      16: if_icmplt     2
      19: return

  public void do2();
    Code:
       0: iconst_0
       1: istore_1
       2: aload_0
       3: iload_1
       4: invokespecial #2                  // Method calc:(I)I
       7: istore_2
       8: iload_2
       9: iconst_2
      10: imul
      11: istore_1
      12: iload_1
      13: sipush        1000
      16: if_icmplt     2
      19: return
}
从“Main.java”编译而来
公共班机{
公用干管();
代码:
0:aload_0
1:invokespecial#1//方法java/lang/Object。“:()V
4:返回
公共无效do1();
代码:
0:iconst_0
1:istore_1
2:aload_0
3:iload_1
4:invokespecial#2//方法计算:(I)I
7:istore_2
8:iload_2
9:iconst_2
10:imul
11:istore_1
12:iload_1
13:sipush 1000
16:if_icmplt 2
19:返回
公共无效do2();
代码:
0:iconst_0
1:istore_1
2:aload_0
3:iload_1
4:invokespecial#2//方法计算:(I)I
7:istore_2
8:iload_2
9:iconst_2
10:imul
11:istore_1
12:iload_1
13:sipush 1000
16:if_icmplt 2
19:返回
}

有人知道这里是否真的没有区别,或者可以解决讨论吗?

没有区别。由于没有分配新的对象,因此这两个对象都与垃圾收集器没有任何交互<代码>整数变量在堆栈上分配


这反映在字节码中,因为每个字节码都是相同的。

正如您在检查字节码时所看到的,Java中没有差异。您可能会在其他语言中看到一些差异,例如,如果用C编写相同的代码

所以在你的例子中,争论是关于化妆品和可维护性的

我认为使用最具体的范围级别(如
do2
)更好,如下所示:

  • 它改进了所需变量的局部性
  • 它使重构时代码块的提取更容易
  • 对于更复杂的示例确实会产生不同的字节码,并且在循环中分配变量的成本不太高(例如,对于大型复合对象,您可能不希望这样做)的情况,这是一个好习惯


不同的观点,但在您的示例中,我甚至认为可以在下一个表达式中内联
val
变量。不这样做只会增加噪音。但我认为这样做是为了示例或打印值。

字节码说明了一切:没有区别。最终的性能将取决于JVM和您使用的硬件。我支持“尽可能缩小范围”的情况-开发人员倾向于使用他们可以访问的变量做愚蠢的事情,并且总是很高兴在一个地方使用所有必要的变量修复bug,而不需要查看太多的地方。:-)上面的例子应该没有什么不同。如果您有两个块,每个块都有自己的局部变量,这可以帮助编译器认识到,这些变量可以共享相同的堆栈空间,因为它们不可能同时存在(尽管一个好的编译器无论如何都可以找到这一点),结果是,您可以在堆栈溢出之前将更多调用放入堆栈中,因为每个堆栈帧都稍微小一些。但是,如果你离成功非常近,而且你没有在一个嵌入式目标上,那么你已经遇到了问题。