论坛首页 Java企业应用论坛

一段代码引起项目失败

浏览 19297 次
精华帖 (0) :: 良好帖 (1) :: 新手帖 (3) :: 隐藏帖 (0)
作者 正文
   发表时间:2011-03-10  
kidneyball 写道
chenyongxin 写道
kidneyball 写道
没看出跟垃圾回收有什么关系。按这个逻辑来看,加入代码3后,只要Map里面有东西,就会导致循环调用,引起StackOverflow。是调用栈的overflow。

而在没加入之前,Map的自引用应该是不会阻碍垃圾回收的。就算有什么东西没有被垃圾回收,也是出现Out Of Memory,而不是StackOverflow。重不重启虚拟机跟这里的问题完全没有关系。

呵呵,谢谢指点,你可以参考下jvm对强引用对象的垃圾回收机制,不过我只是提到垃圾回收而已,只是想说明这段代码不但存在死循环的递归调用,对于内存的释放也是存在点问题的。系统中这样的少量代码存在并且少量被调用不会引起问题,但是大量存在我不相信不会out of memory 否则教科书都是骗人的--什么垃圾回收,内存释放 毛用!


教科书倒没有骗人。。。不过这里又跟强引用对象有什么关系呢,莫非jvm的垃圾回收机制是引用计数?

现在都不用引用计数了,而是查找引用链了,到根的引用链如果不能到达认为是垃圾,不过这是我好几年前的知识了,最近没有仔细研究过,如果有问题请指教。
0 请登录后投票
   发表时间:2011-03-10   最后修改:2011-03-10
kidneyball 写道
kidneyball 写道
chenyongxin 写道
kidneyball 写道
没看出跟垃圾回收有什么关系。按这个逻辑来看,加入代码3后,只要Map里面有东西,就会导致循环调用,引起StackOverflow。是调用栈的overflow。

而在没加入之前,Map的自引用应该是不会阻碍垃圾回收的。就算有什么东西没有被垃圾回收,也是出现Out Of Memory,而不是StackOverflow。重不重启虚拟机跟这里的问题完全没有关系。

呵呵,谢谢指点,你可以参考下jvm对强引用对象的垃圾回收机制,不过我只是提到垃圾回收而已,只是想说明这段代码不但存在死循环的递归调用,对于内存的释放也是存在点问题的。系统中这样的少量代码存在并且少量被调用不会引起问题,但是大量存在我不相信不会out of memory 否则教科书都是骗人的--什么垃圾回收,内存释放 毛用!


教科书倒没有骗人。。。不过这里又跟强引用对象有什么关系呢,莫非jvm的垃圾回收机制是引用计数?

你的意思是下面的类会出现泄漏?
class A {
    private A a;
    public A() {
        a = this;
    }
}


我的意思是:
Map infoMap = new HashMap();
class 其它类{
while(true){//假定有个守护进程

  Map m = infoMap;//new HashMap();  按照我这边代码的效果修改下
  infoMap.put("m",m);
  m.put("i",infoMap);
  Thread.sleep("半天");
}}
如果一直不关机的话。。。
0 请登录后投票
   发表时间:2011-03-10   最后修改:2011-03-10
引用

我的意思是:
Map infoMap = new HashMap();
while(true){
  Map m = new HashMap();
  infoMap.put("m",m);  //1
  m.put("i",infoMap);  //2
  Thread.sleep("半天");
}
如果一直不关机的话。。。


这不叫泄漏,这是本来就没打算释放,即使你把上面的1,2两处的代码去掉,两个map一样没有释放呀。如果你把这里对infoMap与m的引用都打断,但由于它们之间的循环引用导致没有释放,才叫泄漏。但现在的jvm是能够回收这种孤立的循环引用的。
0 请登录后投票
   发表时间:2011-03-10   最后修改:2011-03-10
kidneyball 写道
引用

我的意思是:
Map infoMap = new HashMap();
while(true){
  Map m = new HashMap();
  infoMap.put("m",m);  //1
  m.put("i",infoMap);  //2
  Thread.sleep("半天");
}
如果一直不关机的话。。。


这不叫泄漏,这是本来就没打算释放,即使你把上面的1,2两处的代码去掉,两个map一样没有释放呀。如果你把这里对infoMap与m的引用都打断,但由于它们之间的循环引用导致没有释放,才叫泄漏。但现在的jvm是能够回收这种孤立的循环引用的。

打个比方嘛:while(true){算是一个守护进程可以了吧,这又扯到什么叫内存溢出了吧。我用语言描述吧就是这个infoMap始终被引用着程序一直在跑,它还被不停的put,get,remove,用代码描述吧,如何用简单的代码描述出来复杂的引用环节?
0 请登录后投票
   发表时间:2011-03-10  
chenyongxin 写道

我的意思是:
Map infoMap = new HashMap();
class 其它类{
while(true){//假定有个守护进程

  Map m = infoMap;//new HashMap();  按照我这边代码的效果修改下
  infoMap.put("m",m);
  m.put("i",infoMap);
  Thread.sleep("半天");
}}
如果一直不关机的话。。。


……呃,算了,越改越看不懂了,如果m=infoMap,不就是infoMap里有两个键分别持有了自己么。扯远了,垃圾回收不是本贴的重点吧。

关于项目,个人的建议是发布前一定要进行封版测试,包括通过esb所依赖的外部环境也需要有严格的版本控制。如果另一端不是本项目组的也要事先沟通好。从代码看,代码3加入后只要被运行到,发现错误的几率是很大的,而且调用栈信息完整的话完全可以快速响应。主要问题在于“黑箱作业”四个字上。
0 请登录后投票
   发表时间:2011-03-10   最后修改:2011-03-10
kidneyball 写道
chenyongxin 写道

我的意思是:
Map infoMap = new HashMap();
class 其它类{
while(true){//假定有个守护进程

  Map m = infoMap;//new HashMap();  按照我这边代码的效果修改下
  infoMap.put("m",m);
  m.put("i",infoMap);
  Thread.sleep("半天");
}}
如果一直不关机的话。。。


……呃,算了,越改越看不懂了,如果m=infoMap,不就是infoMap里有两个键分别持有了自己么。扯远了,垃圾回收不是本贴的重点吧。

关于项目,个人的建议是发布前一定要进行封版测试,包括通过esb所依赖的外部环境也需要有严格的版本控制。如果另一端不是本项目组的也要事先沟通好。从代码看,代码3加入后只要被运行到,发现错误的几率是很大的,而且调用栈信息完整的话完全可以快速响应。主要问题在于“黑箱作业”四个字上。

我在前面应该描述过 public SendMessageParam(Map<String, Object> data) {  
        this.data = data;  
} 看来使用bulidermap是重新建立一个map拷贝 但实际上是引用拷贝,在后续对拷贝操作有自引用,难免期望深拷贝后对infoMap进行缓存操作的。

不过垃圾回收的确不是本帖的重点,不过一时兴起多写了几字。
0 请登录后投票
   发表时间:2011-03-11   最后修改:2011-03-11
如果不计垃圾回收的问题(还未确定有没有)的话,代码1、2、3从设计上并没有太大的问题,毕竟用wraper的方式去构造对象是很常见的。如果谨慎的话,SendMessageParam这个类应该叫做XXXXWraper,或者实现一个叫XXXXWraper的接口。但在实际项目中很难强制推行。

而buildSendMessageParam,老实说也没有作任何暗示自己会作深拷贝。代码1在能正确实现业务需求的前提下,这样使用infoMap也是没有问题的(因为这就是业务问题所需的处理逻辑)。而且它们正确运行了半年多。即使一定要觉得代码1、2、3有问题,事实上在项目管理中也难以用可操作的方式来改进。

倒是新加入的代码就很有问题:

1. 程序员3有权力在项目发布前黑箱操作,其行为会影响本项目但似乎本项目对其质量不可控,这显然对本项目是个风险,应该提早预见并且用行政手段来干预。

2. 程序员3对“覆盖toString()”这个行为的风险没有概念,通常来说toString里面不应该再调用另一个不明底细的对象的toString。本身就有可能引起严重的深度递归的性能问题,而且只要出现循环引用(很常见,特别涉及多个节点的引用链)就会出现调用栈溢出。而仅仅是为了“记录日志”冒这个风险实在不值。

这两点都是可以做出可行的改进的,建议是从这里着手。
0 请登录后投票
   发表时间:2011-03-11  
一般 ORM 的实体映射中一对多双向关联的 toString 就很有可能会出现这样的情况。
0 请登录后投票
   发表时间:2011-03-11  
引用
代码片段2和代码片段3是工程师1编写的,代码片段1是工程师2编写的。很明显工程师2没有理解工程师1的目的,也没有仔细阅读代码,所以写出了Map自引用的bug。当然工程师1对代码片段3的处理上是有问题的。


工程师2需要看工程师1的代码吗? 工程师2只是针对接口编程,看的应该只是文档。
1 请登录后投票
   发表时间:2011-03-11  
问一个问题啊,我在用tomcat6+ssh+jotm测试jta时,如果两个数据源都是基于MySQL的就可以,如果一个是MySQL,一个是SqlServer,就不能回滚,请问哪位遇到过吗????
0 请登录后投票
论坛首页 Java企业应用版

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