论坛首页 Java企业应用论坛

代码难道不是这么写的?

浏览 65871 次
该帖已经被评为良好帖
作者 正文
   发表时间:2010-07-30  
coffeesweet 写道
来看s 的

把java的for ... in...的源码看一下就明白了

能在详细解释下么,对待新人要耐心一些么, 我还是不知道s 干吗的?
0 请登录后投票
   发表时间:2010-07-30  
那你可以写个小程序测试一下,比如说设置-Xms32m -Xmx32m,然后用两个方法往里放一些大的字符串,结果应该是基本相同的。

你们的评审人员说的应该是有问题的,但是你要注意他有可能也在看这个帖子,呵呵。
0 请登录后投票
   发表时间:2010-07-30  
fengfeng925 写道
qiushily2030 写道
A a;
申明在内部,每次遍历都申明一个a的变量.  
申明在外面 只申明一次 一直复用.  申明也是要资源的..
一个变量就相当于一个指针,循环1亿次,LZ你说呢.
评审官说的还是有理的,不过这个得看需求.  JVM回收是在内存不足的时候.
  还是不要想当然了,我就那么干过一回. 程序是最真实的答案...

简直是TMD胡扯。


JVM回收是在内存不足的时候.这个已经修正. 不是内存不足才调用。。
你说我胡扯? 那for里定义的局部变量放哪里?不要也和上面一个的观点一样:只申明一次,后调用索引.
这个我不可理解。。
0 请登录后投票
   发表时间:2010-07-30  
FreeWhere 写道
其实重点是在垃圾回收这里


看到了一些错误知识 误导了我.  垃圾回收机制还是去看 深入java虚拟机
0 请登录后投票
   发表时间:2010-07-30  
qiushily2030 写道
fengfeng925 写道
qiushily2030 写道
A a;
申明在内部,每次遍历都申明一个a的变量.  
申明在外面 只申明一次 一直复用.  申明也是要资源的..
一个变量就相当于一个指针,循环1亿次,LZ你说呢.
评审官说的还是有理的,不过这个得看需求.  JVM回收是在内存不足的时候.
  还是不要想当然了,我就那么干过一回. 程序是最真实的答案...

简直是TMD胡扯。


JVM回收是在内存不足的时候.这个已经修正. 不是内存不足才调用。。
你说我胡扯? 那for里定义的局部变量放哪里?不要也和上面一个的观点一样:只申明一次,后调用索引.
这个我不可理解。。

恩,说你胡扯是我不对。
放在for循环里,每循环一次,声明一个局部变量,一次循环结束,局部变量消失。进入第二次循环。再声明一个变量,消失的那个变量内存已经回来了,不是说重复声明多个a变量。
0 请登录后投票
   发表时间:2010-07-30  
qiushily2030 写道
fengfeng925 写道
qiushily2030 写道
A a;
申明在内部,每次遍历都申明一个a的变量.  
申明在外面 只申明一次 一直复用.  申明也是要资源的..
一个变量就相当于一个指针,循环1亿次,LZ你说呢.
评审官说的还是有理的,不过这个得看需求.  JVM回收是在内存不足的时候.
  还是不要想当然了,我就那么干过一回. 程序是最真实的答案...

简直是TMD胡扯。


JVM回收是在内存不足的时候.这个已经修正. 不是内存不足才调用。。
你说我胡扯? 那for里定义的局部变量放哪里?不要也和上面一个的观点一样:只申明一次,后调用索引.
这个我不可理解。。



主要你误解了a变量在遍历中,有入栈和出栈的操作。并没有重复开辟局部变量,再说JVM优化之后,不会重复创建。
0 请登录后投票
   发表时间:2010-07-30  
kongxx 写道
那你可以写个小程序测试一下,比如说设置-Xms32m -Xmx32m,然后用两个方法往里放一些大的字符串,结果应该是基本相同的。

你们的评审人员说的应该是有问题的,但是你要注意他有可能也在看这个帖子,呵呵。



设置Heap的参数没有一点意义,倒是可以设置-Xss线程的栈空间大小来试试。
0 请登录后投票
   发表时间:2010-07-30  
qiushily2030 写道
fengfeng925 写道
qiushily2030 写道
A a;
申明在内部,每次遍历都申明一个a的变量.  
申明在外面 只申明一次 一直复用.  申明也是要资源的..
一个变量就相当于一个指针,循环1亿次,LZ你说呢.
评审官说的还是有理的,不过这个得看需求.  JVM回收是在内存不足的时候.
  还是不要想当然了,我就那么干过一回. 程序是最真实的答案...

简直是TMD胡扯。


JVM回收是在内存不足的时候.这个已经修正. 不是内存不足才调用。。
你说我胡扯? 那for里定义的局部变量放哪里?不要也和上面一个的观点一样:只申明一次,后调用索引.
这个我不可理解。。



不可理解就看看字节码,了解下java的方法调用是怎么回事儿,这不就理解了。
0 请登录后投票
   发表时间:2010-07-30  
qiushily2030 写道
fengfeng925 写道
qiushily2030 写道
A a;
申明在内部,每次遍历都申明一个a的变量.  
申明在外面 只申明一次 一直复用.  申明也是要资源的..
一个变量就相当于一个指针,循环1亿次,LZ你说呢.
评审官说的还是有理的,不过这个得看需求.  JVM回收是在内存不足的时候.
  还是不要想当然了,我就那么干过一回. 程序是最真实的答案...

简直是TMD胡扯。


JVM回收是在内存不足的时候.这个已经修正. 不是内存不足才调用。。
你说我胡扯? 那for里定义的局部变量放哪里?不要也和上面一个的观点一样:只申明一次,后调用索引.
这个我不可理解。。


方法的参数连同方法里的变量所占用的空间,在编译时就已经确定了,它决定了运行时一个方法被调用的时侯要分配多大的栈帧。一个变量放在循环外还是循环里,只是在编译期限定了其作用域而已,到了运行时是没有区别的(也许只是在栈帧的local variables区里的位置不同)。
看一下方法的字节码,一切都非常明了:
private final int[] numbers = {1, 2, 3, 4, 5};
	
public void varOutsideLoop() {
	int n;
	for (int i = 0; i < numbers.length; i++) {
		n = numbers[i];
		System.out.println(n);
	}
}
	
public void varInsideLoop() {
	for (int i = 0; i < numbers.length; i++) {
		int n = numbers[i];
		System.out.println(n);
	}
}


对应的字节码:
public void varOutsideLoop();
  Signature: ()V
  Code:
   Stack=2, Locals=3, Args_size=1
   0:	iconst_0
   1:	istore_2
   2:	goto	22
   5:	aload_0
   6:	getfield	#12; //Field numbers:[I
   9:	iload_2
   10:	iaload
   11:	istore_1
   12:	getstatic	#19; //Field java/lang/System.out:Ljava/io/PrintStream;
   15:	iload_1
   16:	invokevirtual	#25; //Method java/io/PrintStream.println:(I)V
   19:	iinc	2, 1
   22:	iload_2
   23:	aload_0
   24:	getfield	#12; //Field numbers:[I
   27:	arraylength
   28:	if_icmplt	5
   31:	return
  LineNumberTable: 
   line 43: 0
   line 44: 5
   line 45: 12
   line 43: 19
   line 47: 31

  LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   0      32      0    this       Lcom/jstudio/learnjvm/classloader/VarInLoop;
   12      10      1    n       I
   2      29      2    i       I


public void varInsideLoop();
  Signature: ()V
  Code:
   Stack=2, Locals=3, Args_size=1
   0:	iconst_0
   1:	istore_1
   2:	goto	22
   5:	aload_0
   6:	getfield	#12; //Field numbers:[I
   9:	iload_1
   10:	iaload
   11:	istore_2
   12:	getstatic	#19; //Field java/lang/System.out:Ljava/io/PrintStream;
   15:	iload_2
   16:	invokevirtual	#25; //Method java/io/PrintStream.println:(I)V
   19:	iinc	1, 1
   22:	iload_1
   23:	aload_0
   24:	getfield	#12; //Field numbers:[I
   27:	arraylength
   28:	if_icmplt	5
   31:	return
  LineNumberTable: 
   line 50: 0
   line 51: 5
   line 52: 12
   line 50: 19
   line 54: 31

  LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   0      32      0    this       Lcom/jstudio/learnjvm/classloader/VarInLoop;
   2      29      1    i       I
   12      7      2    n       I


仔细看字节码就能发现,除了n和循环变量i在local variable table中的位置不同,其他的所有的都没有区别。
varOutsideLoop那个方法里我故意没有初始化n,如果加上初始化,就会多一条iconst和一条istore指令。
至于位置不一样的原因,个人猜测与变量出现的位置有关,n在循环外的版本,它出现在前,所以占据了第一个slot(第0个slot是对象本身,即this);n在循环里的版本,循环变量i出现在前,所以i占据了第一个slot,而n占用的是第二个slot。
除此之外,两个方法没有任何的区别。

mercyblitz 写道

主要你误解了a变量在遍历中,有入栈和出栈的操作。并没有重复开辟局部变量,再说JVM优化之后,不会重复创建。

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

实践中,变量的作用域当然是尽可能小才好,即有利于垃圾收集、也消除了一些潜在的导致bug的隐患。
楼主别理那个“代码评审官”就好……话说如果我遇到这种人这种事,我八成不愿继续干下去……

5 请登录后投票
   发表时间:2010-07-30  
moonranger 写道
qiushily2030 写道
fengfeng925 写道
qiushily2030 写道
A a;
申明在内部,每次遍历都申明一个a的变量.  
申明在外面 只申明一次 一直复用.  申明也是要资源的..
一个变量就相当于一个指针,循环1亿次,LZ你说呢.
评审官说的还是有理的,不过这个得看需求.  JVM回收是在内存不足的时候.
  还是不要想当然了,我就那么干过一回. 程序是最真实的答案...

简直是TMD胡扯。


JVM回收是在内存不足的时候.这个已经修正. 不是内存不足才调用。。
你说我胡扯? 那for里定义的局部变量放哪里?不要也和上面一个的观点一样:只申明一次,后调用索引.
这个我不可理解。。


方法的参数连同方法里的变量所占用的空间,在编译时就已经确定了,它决定了运行时一个方法被调用的时侯要分配多大的栈帧。一个变量放在循环外还是循环里,只是在编译期限定了其作用域而已,到了运行时是没有区别的(也许只是在栈帧的local variables区里的位置不同)。
看一下方法的字节码,一切都非常明了:
private final int[] numbers = {1, 2, 3, 4, 5};
	
public void varOutsideLoop() {
	int n;
	for (int i = 0; i < numbers.length; i++) {
		n = numbers[i];
		System.out.println(n);
	}
}
	
public void varInsideLoop() {
	for (int i = 0; i < numbers.length; i++) {
		int n = numbers[i];
		System.out.println(n);
	}
}


对应的字节码:
public void varOutsideLoop();
  Signature: ()V
  Code:
   Stack=2, Locals=3, Args_size=1
   0:	iconst_0
   1:	istore_2
   2:	goto	22
   5:	aload_0
   6:	getfield	#12; //Field numbers:[I
   9:	iload_2
   10:	iaload
   11:	istore_1
   12:	getstatic	#19; //Field java/lang/System.out:Ljava/io/PrintStream;
   15:	iload_1
   16:	invokevirtual	#25; //Method java/io/PrintStream.println:(I)V
   19:	iinc	2, 1
   22:	iload_2
   23:	aload_0
   24:	getfield	#12; //Field numbers:[I
   27:	arraylength
   28:	if_icmplt	5
   31:	return
  LineNumberTable: 
   line 43: 0
   line 44: 5
   line 45: 12
   line 43: 19
   line 47: 31

  LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   0      32      0    this       Lcom/jstudio/learnjvm/classloader/VarInLoop;
   12      10      1    n       I
   2      29      2    i       I


public void varInsideLoop();
  Signature: ()V
  Code:
   Stack=2, Locals=3, Args_size=1
   0:	iconst_0
   1:	istore_1
   2:	goto	22
   5:	aload_0
   6:	getfield	#12; //Field numbers:[I
   9:	iload_1
   10:	iaload
   11:	istore_2
   12:	getstatic	#19; //Field java/lang/System.out:Ljava/io/PrintStream;
   15:	iload_2
   16:	invokevirtual	#25; //Method java/io/PrintStream.println:(I)V
   19:	iinc	1, 1
   22:	iload_1
   23:	aload_0
   24:	getfield	#12; //Field numbers:[I
   27:	arraylength
   28:	if_icmplt	5
   31:	return
  LineNumberTable: 
   line 50: 0
   line 51: 5
   line 52: 12
   line 50: 19
   line 54: 31

  LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   0      32      0    this       Lcom/jstudio/learnjvm/classloader/VarInLoop;
   2      29      1    i       I
   12      7      2    n       I


仔细看字节码就能发现,除了n和循环变量i在local variable table中的位置不同,其他的所有的都没有区别。
varOutsideLoop那个方法里我故意没有初始化n,如果加上初始化,就会多一条iconst和一条istore指令。
至于位置不一样的原因,个人猜测与变量出现的位置有关,n在循环外的版本,它出现在前,所以占据了第一个slot(第0个slot是对象本身,即this);n在循环里的版本,循环变量i出现在前,所以i占据了第一个slot,而n占用的是第二个slot。
除此之外,两个方法没有任何的区别。

mercyblitz 写道

主要你误解了a变量在遍历中,有入栈和出栈的操作。并没有重复开辟局部变量,再说JVM优化之后,不会重复创建。

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

实践中,变量的作用域当然是尽可能小才好,即有利于垃圾收集、也消除了一些潜在的导致bug的隐患。
楼主别理那个“代码评审官”就好……话说如果我遇到这种人这种事,我八成不愿继续干下去……



恩,谢谢指正,我也仔细想了一下。在楼主的例子中,字节码说明两个局部变量指向不同的地方。不会重复开辟。要想开辟更多,哪么需要不同名称的变量申明。
0 请登录后投票
论坛首页 Java企业应用版

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