java for循环理论_【JAVA】变量声明在循环体内还是循环体外的争论,以及怎样才真正叫『避免在循环体中创建对象』?...

这个两个同学 @罗夏 @llzcmxiaotong 搬出了java生成的字节码,如果大家看得懂。很容易得出结论就是,对于Java来说,在循环外申明变量,效率不会变高。 @JonglyRan 更是说他实际测试过,效率没有差别。

但是有同学说道内存占用问题,认为“循环外申明变量内存占用会小很多”。我有话要说!!!

我想说的是: 循环外申明变量不但效率不会变高,在循环外申明变量,内存占用会更大!不但没有正面作用,反而有负面作用!

如果大家看字节码有困难,我们可以使用反编译工具。很容得出效率不会变高的结论

package test;

/*** Created by zhouhongyang@zbj.com on 8/8/2017.*/

public class VariableInsideOutsideLoopTest {

public void outsideLoop() {

Object o;

int i = 0;

while (++i < 100) {

o = new Object();

o.toString();

}

Object b = 1;

}

public void intsideLoop() {

int i = 0;

while (++i < 100) {

Object o = new Object();

o.toString();

}

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();

o.toString();

}

}

public void intsideLoop() {

int i = 0;

while(true) {

++i;

if(i >= 100) {

Object b = Integer.valueOf(1);

return;

}

Object o = new Object();

o.toString();

}

}

}

纳里?反编译出来的代码一模一样!!! 结论不言而喻

那么他们的性能真正的一模一样吗? 性能除了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/lang/Object.toString:()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/lang/Object.toString:()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 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;

嗯,字节码一模一样,真如前面两位大侠说的那样。

真的一模一样???

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引用的对象是一个大对象时,还是有那么点意义。

我发现这个问题有很多错误的看法且广为传播,近期准备写一个文章,长篇大论讨论这个问题。到时候请大家来捧场。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值