Sau*_*oat 0 java compiler-optimization
让我们看一下这个例子:
String var;
while (...) {
var = ...
// do stuff
}
Run Code Online (Sandbox Code Playgroud)
在本例中,我们创建对对象的引用String,并在循环的每次迭代中为其分配不同的对象。
现在,在另一个例子中:
while (...) {
String var = ...
// do stuff
}
Run Code Online (Sandbox Code Playgroud)
如果我们假设编译器很幼稚,它只会在每次迭代时String分配对堆栈上对象的引用。
或者会吗?这就是我的问题 - (a?) Java 编译器是否执行此优化?我总是将对象声明保留在尽可能广泛的范围内,因为我担心这一点,但如果编译器已经这样做了,那么我的鞋子上就少了一颗鹅卵石。
先感谢您!
它只会在每次迭代时在堆栈上分配对 String 对象的引用。
事情不是这样的。堆栈上的变量,即参数和局部变量,在方法入口处分配(保留)。
例如,如果您有这样的代码:
static void foo() {
String s;
for (int i = 0; i < 5; i++) {
int j = i;
s = String.valueOf(j);
bar(s);
}
for (int j = 0; j < 5; j++) {
int k = j;
s = String.valueOf(k);
bar(s);
}
}
static void bar(String s) {
}
Run Code Online (Sandbox Code Playgroud)
对于该代码,将在堆栈上分配3 个槽1 :
s将位于槽 0 中,并在整个方法中使用慢速
i在第一个循环期间将位于槽 1 中。
j在第一个循环体的持续时间内将位于槽 2 中。
另一个j将在第二个循环期间位于插槽 1 中。
k将在第二个循环体的持续时间内位于槽 2 中。
正如您所看到的,槽 1 和槽 2 被重用,但在方法执行期间没有进行“分配”。仅在方法开始时分配/反转的内存。
1) 一个槽为 4 字节/32 位,即一个或一个引用的大小int(带有压缩的 Oop)。
如果使用 进行编译javac -g Test.java和反汇编javap -v -c Test.class,您将得到(Java 8 的输出):
static void foo();
descriptor: ()V
flags: ACC_STATIC
Code:
stack=2, locals=3, args_size=0
0: iconst_0
1: istore_1
2: iload_1
3: iconst_5
4: if_icmpge 24
7: iload_1
8: istore_2
9: iload_2
10: invokestatic #2 // Method java/lang/String.valueOf:(I)Ljava/lang/String;
13: astore_0
14: aload_0
15: invokestatic #3 // Method bar:(Ljava/lang/String;)V
18: iinc 1, 1
21: goto 2
24: iconst_0
25: istore_1
26: iload_1
27: iconst_5
28: if_icmpge 48
31: iload_1
32: istore_2
33: iload_2
34: invokestatic #2 // Method java/lang/String.valueOf:(I)Ljava/lang/String;
37: astore_0
38: aload_0
39: invokestatic #3 // Method bar:(Ljava/lang/String;)V
42: iinc 1, 1
45: goto 26
48: return
Run Code Online (Sandbox Code Playgroud)
LocalVariableTable:
Start Length Slot Name Signature
9 9 2 j I
14 10 0 s Ljava/lang/String;
2 22 1 i I
33 9 2 k I
38 10 0 s Ljava/lang/String;
26 22 1 j I
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,该行stack=2, locals=3, args_size=0显示它将为局部变量保留 3 个槽。底部LocalVariableTable显示哪个局部变量在哪个字节码指令(范围)的持续时间内使用哪个槽。
移动循环内部的声明s将重新排列变量分配给槽的顺序,即它们使用哪些槽,并更改 的范围的长度s,但仅此而已。
static void foo() {
for (int i = 0; i < 5; i++) {
int j = i;
String s = String.valueOf(j);
bar(s);
}
for (int j = 0; j < 5; j++) {
int k = j;
String s = String.valueOf(k);
bar(s);
}
}
Run Code Online (Sandbox Code Playgroud)
LocalVariableTable:
Start Length Slot Name Signature
9 9 1 j I
14 4 2 s Ljava/lang/String;
2 22 0 i I
33 9 1 k I
38 4 2 s Ljava/lang/String;
26 22 0 j I
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
384 次 |
| 最近记录: |