日期:2014-05-20  浏览次数:20721 次

还是关于java传值和垃圾回收的疑惑
我是一个java初学者,以前一直用C。在java开发中常和C对比来思考一些问题。我知道java中参数的传递都是用传值的方式,如果参数是对象,则传递的是对象地址的拷贝。我看到下面一段程序:
注:这是在一个函数中的一段代码,rs是一个ResultSet,Conmessage是一个定义好的class。
{......
Collection first=new ArrayList();
while(rs.next()){
  Conmessage mess=new Conmessage();
  mess.setName(rs.getString("Name"));
  first.add(mess);}
......}
代码中,mess是一个对象,增加到first中,按理说只是传递了mess的地址吧。那么while中每一个循环结束这个mess对象就应该被释放掉,相应的first里面的地址也就失效,指向的是无效数据了,但是事实上不是这样,好像add(object)是按照参数对象本身的值传值进去的,也就是说mess的析构和ArrayList里面的值毫无关系了?这是为什么?
另外,我猜想java是不是这样处理的:回收mess时看到这个对象仍然被有效的地址引用着(add到first里面的那个新地址),就不回收它了?
请各位为我解释一下,谢谢。


------解决方案--------------------
楼主你的想法有一定道理,但是不够清晰。程序没有问题。这个问题是很微妙的,牵涉到对象的引用和实体的含义。

注意mess对象一旦创建以后,它有一个引用空间,里面存放的值是实体空间的首地址(假定为134568),当使用add方法把它加入ArraryList时,是按照引用的值传递的,也就是说,与ArraryList的元素关联的是首地址为134568的实体空间,而与mess本身的引用空间就没有关系了。

这样一来,当一次循环结束,mess对象就失效了,进入下一次循环时,mess对象被重新定义和创建了(它的引用空间和实体空间都改变了),此时,新的mess对象与上一个mess对象无任何关系。但是,原来的mess对象关联的那个实体空间却不会被当作垃圾处理掉,原因是,它已经与ArraryList的一个元素关联起来了,只有没有名字的实体空间才会被垃圾收集器清理掉,而它实际上是有名字的,这个名字你可以认为就是ArraryList[i](假定它是第i个元素)。
------解决方案--------------------
楼主的猜想是对的:java中的对象只要还被任何一个变量或其他对象所引用,他就永远不会被垃圾回收。正是因为这个原因,在java中才可以安全地从方法中返回对局部对象的引用(C/C++中的类似用法是错误的)。
所以,在java中,你虽然不需要自己管理内存,但要避免长期引用不再使用的对象,尤其要避免循环引用(如a引用b,b又引用a),以便垃圾回收器能及时回收无用对象。
楼主有C语言背景,这对理解java的一些底层机制是很有帮助的,建议读一读《Thinking in JAVA》中的相关章节,里面对JAVA的内存管理有非常精辟的论述。