论坛首页 Java企业应用论坛

代码难道不是这么写的?

浏览 66526 次
该帖已经被评为良好帖
作者 正文
   发表时间:2010-08-03  
for(int i=0; i<20000; i++) {
	Integer iii = new Integer(1000000);
//	System.out.println(iii);
}



Integer iii = new Integer(1000000);
	for(int i=0; i<20000; i++) {
//	System.out.println(iii);
}


在循环内部和外部实例化就相差就很多了。
0 请登录后投票
   发表时间:2010-08-04  
linliangyi2007 写道
看来大家都闲的蛋痛了,评审也可以洗洗睡了。

理论上都没错,但实际上呢?

把大家平时的代码翻出来看看,我就不信人人都是java编译器。



同意...大部分情况停留在实现的飘过...
0 请登录后投票
   发表时间:2010-08-04  
laodizhuq 写道
for(int i=0; i<20000; i++) {
	Integer iii = new Integer(1000000);
//	System.out.println(iii);
}



Integer iii = new Integer(1000000);
	for(int i=0; i<20000; i++) {
//	System.out.println(iii);
}


在循环内部和外部实例化就相差就很多了。


这是另外一回事,在循环内创建对象当然会增加消耗

但是正常场合的正常人类基本不会这么干的。

另,本着使用时声明的原则,把声明放到循环内部其实挺好的,刻意的把声明放在循环外,可能会导致

1 无意中扩大变量了的作用域,延长了变量的声明周期

2 多了一行代码,而且稍稍影响阅读性,随用随取还是最清楚的
0 请登录后投票
   发表时间:2010-08-04  
laodizhuq 写道
for(int i=0; i<20000; i++) {
	Integer iii = new Integer(1000000);
//	System.out.println(iii);
}



Integer iii = new Integer(1000000);
	for(int i=0; i<20000; i++) {
//	System.out.println(iii);
}


在循环内部和外部实例化就相差就很多了。


不当类比
0 请登录后投票
   发表时间:2010-08-04  
换了是我,我直接骂评审傻X,然后潇洒的走人,跟这种傻X你没法合作的。现在有些人就是懂了一点就当令箭。知识体系根本不健全。
0 请登录后投票
   发表时间:2010-08-04  
评审的人无视!这种人多了去了.眼高手低.
0 请登录后投票
   发表时间:2010-08-04  
wei_jing 写道
  for(int i = 0;i<arr.len;i++) {
  A a = arr[i];
  System.out.println(s);
  }
这样写,是不会在堆内存里分配对象了。A a,只是局部变量,在栈内存里分配,是一个对象的引用而已,在循环体内,就多了arr.len个变量,消耗的是栈内存。

循环每次的时候会出栈,消耗还是一份,倒是离开循环的时候彻底从栈上扔掉了。
改成评审人的写法,在外边一个作用域结束前栈上会一直保持引用。占用反而多。
0 请登录后投票
   发表时间:2010-08-04  
ytsmtxxi 写道
看以好几个人都说不可以在for循环里new对象,
请问,下面的代码要怎么样子来改写?
for(int i=0;i<list.size();i++){
    Object[] obj = (Object[]) list.get(i);
    A a = new A();
    a.setXxx((String) obj[0]);
    a.setXxx((Long) obj[1]);
}
谢谢!

没这么一说,直接无视之可也。
0 请登录后投票
   发表时间:2010-08-04  
pekleo 写道
想说的是你没有错,评审官其实也没有错,写法虽然不会带来额外的内存消耗但是要从一个好的coding习惯来说,评审官是对的。

好的习惯来说,评审人是错的,而且是隐蔽的错。
0 请登录后投票
   发表时间:2010-08-04  
RednaxelaFX 写道
moonranger 写道
mercyblitz 写道
主要你误解了a变量在遍历中,有入栈和出栈的操作。并没有重复开辟局部变量,再说JVM优化之后,不会重复创建。

个人认为这种说法也不太对,在一个方法内部不会有所谓的“入栈”和“出栈”操作。方法里,一个变量占用一个local variable table的slot,一个萝卜一个坑。所有这些变量都是在方法结束,堆栈帧被销毁的时侯才真正被“回收”,即使方法结束的时侯它早以超出了自己的作用域……

ECMA-335 CLI是规定方法中每个局部变量占用局部变量区的一个坑,不在方法中复用局部变量区。但它在设计之初就是倾向于使用JIT编译器来实现执行引擎的;虽然中间代码(CIL)中局部变量不复用局部变量区的坑,但实际JIT编译后的代码不关心这个,局部变量的存储分配(栈/寄存器)还是有复用。可以参考.NET CLR的实现。

(以下说明针对概念中的Java虚拟机)

Java虚拟机在设计之初就同时考虑到要能方便的用解释器或JIT编译器来实现。解释器一般做的优化较少,执行过程与字节码中指定的方式基本是直接对应的,所以字节码上就已经需要考虑到实际执行的情况了。Java虚拟机的局部变量区是可复用的,在同一方法里作用域不交叠的局部变量可以分配在同一个局部变量区的slot上。有兴趣的话可以自己做实验验证,也可以参考之前我做的分享的演示稿,20100621的版本。
例子的话,可以看这么一段代码:
public class LocalsDemo {
    // locals = 5
    public static void main(String[] args) { // args in slot 0
        int a = 1;                           // a    in slot 1
        int b = 2;                           // b    in slot 2
        {
            long c = 3;                      // c    in slot 3 (to slot 4)
        }
        {
            int d = 4;                       // d    in slot 3
            int e = 5;                       // e    in slot 4
        }
        for (int i = 0; i < 3; i++) {        // i    in slot 3
            int j = i + 1;                   // j    in slot 4
        }
    }
}

main()方法编译后对应的字节码和元信息是:
public static void main(java.lang.String[]);
  Code:
   Stack=2, Locals=5, Args_size=1
   0:   iconst_1
   1:   istore_1
   2:   iconst_2
   3:   istore_2
   4:   ldc2_w  #2; //long 3l
   7:   lstore_3
   8:   iconst_4
   9:   istore_3
   10:  iconst_5
   11:  istore  4
   13:  iconst_0
   14:  istore_3
   15:  iload_3
   16:  iconst_3
   17:  if_icmpge       31
   20:  iload_3
   21:  iconst_1
   22:  iadd
   23:  istore  4
   25:  iinc    3, 1
   28:  goto    15
   31:  return
  LineNumberTable:
   line 4: 0
   line 5: 2
   line 7: 4
   line 10: 8
   line 11: 10
   line 13: 13
   line 14: 20
   line 13: 25
   line 16: 31

  LocalVariableTable:
   Start  Length  Slot  Name   Signature
   8      0      3    c       J
   10      3      3    d       I
   13      0      4    e       I
   25      0      4    j       I
   15      16      3    i       I
   0      32      0    args       [Ljava/lang/String;
   2      30      1    a       I
   4      28      2    b       I

要在Class文件里看到LocalVariableTable属性表的话,编译Java源码时请加上-g参数。

Java虚拟机中,
方法调用涉及的栈帧push/pop是对Java栈(Java stack),或者有些文档里叫“Java控制栈”(Java control stack),或者叫“Java方法调用栈”;
在方法中局部变量的写/读操作涉及的push/pop则是对操作数栈(operand stack),或者有些文档叫“表达式栈”(expression stack),或者叫求值栈(evaluation stack)。
两个栈的区别可以参考Java虚拟机规范第二版3.5.23.6.2两个小节,我之前的介绍基于栈/寄存器的虚拟机的帖里也有提到,也可以参考前面说的JVM分享的演示稿。

最后上一老图……逃


本贴中最有含金量的回复
特摘出学习
1 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics