一尘不染

哪个循环具有更好的性能?为什么?

java

String s = “”;
for(i=0;i<....){
s = some Assignment;
}

要么

for(i=0;i<..){
    String s = some Assignment;
}

我不需要在循环外再次使用“ s”。第一个选项可能更好,因为不会每次都初始化一个新的String。但是,第二个结果将导致变量的范围仅限于循环本身。

编辑:回应米尔豪斯的回答。在循环中将String分配给常量是没有意义的吗?不,这里的“某些分配”是指从要迭代的列表中获得的变化值。

另外,问题不是因为我担心内存管理。只想知道哪个更好。


阅读 205

收藏
2020-09-09

共1个答案

一尘不染

范围最好

使用第二个选项:

for ( ... ) {
  String s = ...;
}

范围不影响性能

如果您使用JDK的javap工具反汇编每个编译器的代码,则在两种情况下,您都将看到循环编译为完全相同的JVM指令。还要注意,Brian R.Bondy“选项3”与选项1相同。使用更紧密的示波器时,不会在堆栈中添加或删除任何多余的东西,并且两种情况下在堆栈上都使用相同的数据。

避免过早初始化

两种情况之间的唯一区别是,在第一个示例中,变量s被不必要地初始化。这是与变量声明的位置不同的问题。这会增加两条浪费的指令(以加载字符串常量并将其存储在堆栈帧插槽中)。一个好的静态分析工具会警告您,您永远不会读取分配给它的值s,而好的JIT编译器可能会在运行时忽略它。

您可以简单地通过使用空声明(即String s;)来解决此问题,但这被认为是不好的做法,并且还会在下面讨论另外一个副作用。

通常,将伪造的值(如like
null)分配给变量只是为了掩盖编译器错误,该错误是在未初始化变量的情况下读取变量的。该错误可以作为变量范围太大的暗示,并且在需要接收有效值之前先声明了该错误。空声明迫使您考虑每个代码路径;请勿通过分配虚假值来忽略此有价值的警告。

节省堆叠插槽

如前所述,尽管两种情况下的JVM指令都相同,但是有一个细微的副作用,使它在JVM级别上最好使用尽可能有限的范围。这在该方法的“局部变量表”中可见。考虑一下如果您有多个循环,并且在不必要的大范围内声明了变量,会发生什么情况:

void x(String[] strings, Integer[] integers) {
  String s;
  for (int i = 0; i < strings.length; ++i) {
    s = strings[0];
    ...
  }
  Integer n;
  for (int i = 0; i < integers.length; ++i) {
    n = integers[i];
    ...
  }
}

变量sn可以在它们各自的循环中声明,但是由于它们不是,因此编译器在堆栈帧中使用两个“槽”。如果在循环内声明它们,则编译器可以重用同一插槽,从而使堆栈帧更小。

真正重要的是

但是,这些问题大多数都不重要。一个好的JIT编译器将看到不可能读取您浪费分配的初始值,并且无法优化分配。在此处保存插槽,否则将无法建立或破坏您的应用程序。

重要的是使代码易于阅读且易于维护,在这方面,使用有限的范围显然更好。变量的范围越小,就越容易理解其用法以及对代码进行的更改将产生何种影响。

2020-09-09