java循环定义变量_【JAVA】变量声明在循环体内还是循环体外的争论,以及怎样才真正叫『。。。
这个两个同学 @罗夏 @llzcmxiaotong 搬出了java⽣成的字节码,如果⼤家看得懂。很容易得出结论就是,对于Java来说,在循环外申明变量,效率不会变⾼。 @JonglyRan 更是说他实际测试过,效率没有差别。
但是有同学说道内存占⽤问题,认为“循环外申明变量内存占⽤会⼩很多”。我有话要说
我想说的是: 循环外申明变量不但效率不会变⾼,在循环外申明变量,内存占⽤会更⼤!不但没有正⾯作⽤,反⽽有负⾯作⽤!
如果⼤家看字节码有困难,我们可以使⽤反编译⼯具。很容得出效率不会变⾼的结论
package test;
/*** Created by zhouhongyang@zbj on 8/8/2017.*/
public class VariableInsideOutsideLoopTest {
public void outsideLoop() {
Object o;
int i = 0;
while (++i < 100) {
o = new Object();
}
Object b = 1;
}
public void intsideLoop() {
int i = 0;
while (++i < 100) {
Object o = new Object();
}
Object b = 1;
}
}
上⾯的代码编译成class,反编译出来的样⼦是这样的:
Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//
package test;
public class VariableInsideOutsideLoopTest {
public VariableInsideOutsideLoopTest() {
}
public void outsideLoop() {
int i = 0;
while(true) {
++i;
if(i >= 100) {
Object b = Integer.valueOf(1);
return;
}
Object o = new Object();
}
}
public void intsideLoop() {
int i = 0;
while(true) {
++i;
if(i >= 100) {
Object b = Integer.valueOf(1);
return;
}
Object o = new Object();
}
}
}
纳⾥?反编译出来的代码⼀模⼀样 结论不⾔⽽喻
那么他们的性能真正的⼀模⼀样吗? 性能除了cpu时间以外,还有个指标就是内存占⽤。
没办法,我也只能祭出神器javap了 (有了javap,java性能撕逼必胜,不会的⼤家请google学习⼀下) public void outsideLoop();
Code:
0: iconst_0
1: istore_2
2: iinc 2, 1
5: iload_2
6: bipush 100
8: if_icmpge 27
11: new #2 // class java/lang/Object
14: dup
15: invokespecial #1 // Method java/lang/Object."":()V
18: astore_1
19: aload_1
20: invokevirtual #3 // Method java/String:()Ljava/lang/String; 23: pop
24: goto 2
27: iconst_1
28: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 31: astore_3
32: return
LocalVariableTable:
Start Length Slot Name Signature
19 5 1 o Ljava/lang/Object;
0 33 0 this Ltest/VariableInsideOutsideLoopTest;
2 31 2 i I
32 1 3 b Ljava/lang/Object;
public void intsideLoop();
Code:
0: iconst_0
1: istore_1
2: iinc 1, 1
5: iload_1
6: bipush 100
8: if_icmpge 27
11: new #2 // class java/lang/Object
14: dup
15: invokespecial #1 // Method java/lang/Object."":()V
18: astore_2
19: aload_2
20: invokevirtual #3 // Method java/String:()Ljava/lang/String;
23: pop
24: goto 2
27: iconst_1
28: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
31: astore_2
32: return
LocalVariableTable:
Start Length Slot Name Signaturejava valueof
19 5 2 o Ljava/lang/Object;
0 33 0 this Ltest/VariableInsideOutsideLoopTest;
2 31 1 i I
32 1 2 b Ljava/lang/Object;
嗯,字节码⼀模⼀样,真如前⾯两位⼤侠说的那样。
真的⼀模⼀样
LocalVariableTable:
Start Length Slot Name Signature
19 5 1 o Ljava/lang/Object;
0 33 0 this Ltest/VariableInsideOutsideLoopTest;
2 31 2 i I
32 1 3 b Ljava/lang/Object; //
Start Length Slot Name Signature
19 5 2 o Ljava/lang/Object;
0 33 0 this Ltest/VariableInsideOutsideLoopTest;
2 31 1 i I
32 1 2 b Ljava/lang/Object; //
看到差别了吗? outsideLoop在stack frame中定义了4个slot, ⽽intsideLoop只定义了3个slot
outsideLoop中,变量o和b分别占⽤了不同的slot,在intsideLoop中,变量o和b复⽤⼀个slot。
所以,outsideLoop的stack frame⽐intsideLoop多占⽤4个字节内存(⼀个slot占⽤4个字节,如果我没有记错)
真的就只有4个字节的差别?
由于在intsideLoop中,o和b复⽤了同⼀个slot,所以,当b使⽤slot 2的时候,这是变量o已经“不复存在”,所以o原来引⽤的对象就没有任何引⽤,它有可能⽴即被GC回收(注意是有可能,不是⼀定),腾出所占⽤heap内存。
所以,intsideLoop存在可能,在某些时间点,使⽤的heap内存⽐outsideLoop少。
当然这个例⼦中少的内存微不⾜道,但是假设这个⽅法执⾏时间很长,o引⽤的对象是⼀个⼤对象时,还是有那么点意义。
我发现这个问题有很多错误的看法且⼴为传播,近期准备写⼀个⽂章,长篇⼤论讨论这个问题。到时候请⼤家来捧场。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。