锁定老帖子 主题:代码难道不是这么写的?
该帖已经被评为良好帖
|
|
---|---|
作者 | 正文 |
发表时间:2010-07-28
其实那个评判这样说,的的确确是有点过了,这样是不会耗多少的内存的,都只是在栈里面操作。
不过,如果把那个变量声明到外面会少分配内存好多次,如果真的是要遍历成千上万次的话,他的话不是没有道理的。 |
|
返回顶楼 | |
发表时间:2010-07-28
FlounyCaesar 写道 XTU_xiaoxin 写道 今天代码评审时,评审官看见如下代码,把我是一顿好骂(代码是我写的),大致形式如下:
for(int i = 0;i<arr.len;i++) { A a = arr[i]; System.out.println(s); } 骂我内容:说我会不会编程,把变量写在for循环里面,说如果遍历多次的话会占用大量的堆内存。 要我改成如下形式: A a = null; for(int i = 0;i<arr.len;i++) { a = arr[i]; System.out.println(s); } 其实我也不知道他说的对不对,但他让我改的那种方式是绝对没问题的。但我觉得如果变量写在for循环里面,如果没被用到,应该会被垃圾回收的,不会占用太多堆内存吧。 其实,我看了一些大牛写的代码,他们都把变量写在for里面,只是说不要在for里new对象就行了,我很纳闷。。。。。 从这个代码片段上看不出什么问题,因为对象a并未被使用,而楼主又要创建出arr.len个数量的a对象,不知道用意是什么,其实从这段代码看的话,arr.len在10万以内是不会有什么问题的,关键他创建这些对象的作用是什么 拜託,這有創建出arr.len个数量的a对象? |
|
返回顶楼 | |
发表时间:2010-07-28
aws 写道 临时变量超出作用域,其引用的内存就可以回收了,而且JVM对这种情况有优化
如果生命周期变长,反而有可能多次回收收不掉,进入老年代 按LZ评审官的写法,反而会多一条初始化A的指令 LZ的评审官多半是做C出来的吧 有两方面考虑 楼主的写法,栈变量会变多,栈空间消耗大, 而 面试官 的方式 有可能造成 变量进入老年代 如果内存资源有限的情况下,array比较大的时,楼主的情况会占大量栈内存. 但是,综上看 还是要看你的代码到底是哪种环境用,如果是j2me之类 除特殊要求 需要注意这些外,个人觉得楼主的代码更漂亮! 而且实际项目中,也很少这么抠,不然一个项目不知道会要写多久,而且程序员素质要多高! |
|
返回顶楼 | |
发表时间:2010-07-28
yangmen066 写道 aws 写道 临时变量超出作用域,其引用的内存就可以回收了,而且JVM对这种情况有优化
如果生命周期变长,反而有可能多次回收收不掉,进入老年代 按LZ评审官的写法,反而会多一条初始化A的指令 LZ的评审官多半是做C出来的吧 有两方面考虑 楼主的写法,栈变量会变多,栈空间消耗大, 而 面试官 的方式 有可能造成 变量进入老年代 如果内存资源有限的情况下,array比较大的时,楼主的情况会占大量栈内存. 但是,综上看 还是要看你的代码到底是哪种环境用,如果是j2me之类 除特殊要求 需要注意这些外,个人觉得楼主的代码更漂亮! 而且实际项目中,也很少这么抠,不然一个项目不知道会要写多久,而且程序员素质要多高! 请教下,怎么会大量占用栈内存?不就一个局部变量吗?何来的大量占用? |
|
返回顶楼 | |
发表时间:2010-07-28
为了免的某些人再扯淡,我将编译后的字节码贴出来:
public class Test { public static void main(String[] args) { for (int i = 0; i < args.length; i++) { String a = args[i]; System.out.println(a); } String a; for (int i = 0; i < args.length; i++) { a = args[i]; System.out.println(a); } } }
JDK6编译的字节码:
public class Test { // compiled from: Test.java // access flags 1 public <init>()V L0 (0) LINENUMBER 3 L0 ALOAD 0 INVOKESPECIAL java/lang/Object.<init>()V RETURN L1 (4) LOCALVARIABLE this Lcom/taobao/notify/timeserver/client/Test; L0 L1 0 MAXSTACK = 1 MAXLOCALS = 1 // access flags 9 public static main([Ljava/lang/String;)V L0 (0) LINENUMBER 6 L0 ICONST_0 ISTORE 1 L1 (3) GOTO L2 L3 (5) LINENUMBER 7 L3 ALOAD 0 ILOAD 1 AALOAD ASTORE 2 L4 (10) LINENUMBER 8 L4 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; ALOAD 2 INVOKEVIRTUAL java/io/PrintStream.println(Ljava/lang/String;)V L5 (14) LINENUMBER 6 L5 IINC 1 1 L2 (16) ILOAD 1 ALOAD 0 ARRAYLENGTH IF_ICMPLT L3 L6 (21) LINENUMBER 11 L6 ICONST_0 ISTORE 2 L7 (24) GOTO L8 L9 (26) LINENUMBER 12 L9 ALOAD 0 ILOAD 2 AALOAD ASTORE 1 L10 (31) LINENUMBER 13 L10 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; ALOAD 1 INVOKEVIRTUAL java/io/PrintStream.println(Ljava/lang/String;)V L11 (35) LINENUMBER 11 L11 IINC 2 1 L8 (37) ILOAD 2 ALOAD 0 ARRAYLENGTH IF_ICMPLT L9 L12 (42) LINENUMBER 15 L12 RETURN L13 (44) LOCALVARIABLE args [Ljava/lang/String; L0 L13 0 LOCALVARIABLE i I L1 L6 1 LOCALVARIABLE a Ljava/lang/String; L4 L5 2 LOCALVARIABLE a Ljava/lang/String; L10 L8 1 LOCALVARIABLE i I L7 L12 2 MAXSTACK = 2 MAXLOCALS = 3 } args.length就是一条指令ARRAYLENGTH,哪怕你存到变量len里,也要一条LOAD指令。 两个循环区别仅仅在局部变量的索引上。 |
|
返回顶楼 | |
发表时间:2010-07-28
最后修改:2010-07-30
yangmen066 写道 aws 写道 临时变量超出作用域,其引用的内存就可以回收了,而且JVM对这种情况有优化
如果生命周期变长,反而有可能多次回收收不掉,进入老年代 按LZ评审官的写法,反而会多一条初始化A的指令 LZ的评审官多半是做C出来的吧 有两方面考虑 楼主的写法,栈变量会变多,栈空间消耗大, 而 面试官 的方式 有可能造成 变量进入老年代 如果内存资源有限的情况下,array比较大的时,楼主的情况会占大量栈内存. 要知道这个是在一个线程里操作, 当一个局部变量不在使用,它其实就可以出栈(操作数栈)了,所以同一时间,该线程只会有一个这样个的变量在栈里,只是在不停的出栈和入栈。 而且评审官的方式,让变量所指之对象进老年代更无从谈起,因为A a只是一个引用,其所指的对象是在array中,所以面试官的方式缺点应该是扩大了a的可见性,其实没有这个必要, 当然面试官的方式更不会有什么内存扩大的事情出来。 |
|
返回顶楼 | |
发表时间:2010-07-28
diferent 写道 这种事情是要看情况而定的
不能一概而论 1.for(int i = 0;i<arr.len;i++) { 2. A a = arr[i]; 3. System.out.println(s); // s? 当它不存在吧 4. } 上面这样做的优点是 缩小了引用A的作用范围. 1.A a = null; 2.for(int i = 0;i<arr.len;i++) { 3. a = arr[i]; 4. System.out.println(s); 5.} 这样做的话 , 只声明了一次. 但是在一些特殊情况下 比如 所在的函数很长, 需要执行很多的时间. 那么,a变量将一直指向arr[arr.len-1],又恰好 arr[arr.len-1]在后来某操作 而被弹出了arr数组.也就是从arr数组中被删除了. 但是a还没有过期,所以即使GC在这段时间内工作了,这个对象也不会被回收. 大概就是如此,也不算是问题,除非极特殊,这个A对象是否回收对系统有相当大的影响. 所以评审这种代码是很无聊的. 评审的时候找出一代码潜在问题, 重新组织代码的结构远比要纠结这种问题有意义的多 你的解释没有说,不过在这个场景下面,上后两种语义是一致的。除非A a在后面又被用到。 |
|
返回顶楼 | |
发表时间:2010-07-28
最后修改:2010-07-30
dennis_zane 写道 为了免的某些人再扯淡,我将编译后的字节码贴出来: args.length就是一条指令ARRAYLENGTH,哪怕你存到变量len里,也要一条LOAD指令。 两个循环区别仅仅在局部变量的索引上。 ====================== 有人是做j2me之类的移动设备的, 一般上他们认为 for (int k =0; k < ArrayList.getSize();k++) 比 int len = ArrayList.getSize() for (int k =0; k < len;k++)要多出多个个方法栈帧(方法调用栈)的操作。 但是在j2se上,这种消耗根本不值一提。 而对于array.length.这就是一个变量访问,这个是肯定的。不会有什么消耗(即使是在j2me中) |
|
返回顶楼 | |
发表时间:2010-07-28
ahuaxuan 写道 yangmen066 写道 aws 写道 临时变量超出作用域,其引用的内存就可以回收了,而且JVM对这种情况有优化
如果生命周期变长,反而有可能多次回收收不掉,进入老年代 按LZ评审官的写法,反而会多一条初始化A的指令 LZ的评审官多半是做C出来的吧 有两方面考虑 楼主的写法,栈变量会变多,栈空间消耗大, 而 面试官 的方式 有可能造成 变量进入老年代 如果内存资源有限的情况下,array比较大的时,楼主的情况会占大量栈内存. 要知道这个是在一个线程里操作, 当一个局部变量不在使用,它其实就可以出栈了,所以同一时间,该线程只会有一个这样个的变量在栈里,只是在不停的出栈和入栈。 而且评审官的方式,让变量所指之对象进老年代更无从谈起,因为A a只是一个引用,其所指的对象是在array中,所以面试官的方式缺点应该是扩大了a的可见性,其实没有这个必要, 当然面试官的方式更不会有什么内存扩大的事情出来。 呵呵,我说错了,误导大家了 |
|
返回顶楼 | |
发表时间:2010-07-28
yangmen066 写道 aws 写道 临时变量超出作用域,其引用的内存就可以回收了,而且JVM对这种情况有优化
如果生命周期变长,反而有可能多次回收收不掉,进入老年代 按LZ评审官的写法,反而会多一条初始化A的指令 LZ的评审官多半是做C出来的吧 有两方面考虑 楼主的写法,栈变量会变多,栈空间消耗大, 而 面试官 的方式 有可能造成 变量进入老年代 如果内存资源有限的情况下,array比较大的时,楼主的情况会占大量栈内存. 但是,综上看 还是要看你的代码到底是哪种环境用,如果是j2me之类 除特殊要求 需要注意这些外,个人觉得楼主的代码更漂亮! 而且实际项目中,也很少这么抠,不然一个项目不知道会要写多久,而且程序员素质要多高! 其实,修改后,除了作用域的不同,没有什么不同。 关于对象的所在周期,不是有变量决定。而是看之前模个时候,arr[i] = new A();new出来的A对象时候否不再可达。这里的A a=arr[i],仅仅是一个引用。在栈中,这个变量的空间大小就是引用对象空间大小-4字节。当进入时,入栈。不再调用时,出栈。否者,递归程序没有办法完成任务 |
|
返回顶楼 | |