锁定老帖子 主题:一段代码引起项目失败
精华帖 (0) :: 良好帖 (1) :: 新手帖 (3) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2011-03-10
最后修改:2011-03-10
因为一个小小的bug导致失败,记录下来,由于平台是公司自有业务,所以是处于不断开发和维护中的,难免前人挖坑后人跌倒。 上代码: 代码片段1:
Map infoMap = new HashMap(); SendMessageParam sendMessageParam = buildSendMessageParam(map,infoMap); ... ... //很长一段逻辑 ... ... infoMap.put("sendMessageParam", sendMessageParam); buildSendMessageParam函数处理方法: 代码片段2:
SendMessageParam buildSendMessageParam(Map map, Map infoMap) { SendMessageParam sendMessageParam = new SendMessageParam(infoMap); ... ... ... return sendMessageParam; } SendMessageParam类的构造方法: 代码片段3:
public SendMessageParam(Map<String, Object> data) { this.data = data; }
代码片段2和代码片段3是工程师1编写的,代码片段1是工程师2编写的。很明显工程师2没有理解工程师1的目的,也没有仔细阅读代码,所以写出了Map自引用的bug。当然工程师1对代码片段3的处理上是有问题的。 代码运行近半年没有问题,虽然Map自引用会导致垃圾难以回收(注:希望通过buildSendMessageParam生成新对象进行操作,而“缓存”老对象的话或者始终存在从根到infoMap的引用链),但是因为基本每天都有项目或者小需求发布虚拟机重启,内存溢出没有暴露出来;或者代码片段1所在的线程完成任务后Map自引用失去了引用链而被垃圾回收。
一切似乎很正常,但是悲剧发生在新年的第一个项目上,工程师3为了在日志里记录infoMap的数据以备分析,做出了以下代码:
public class SendMessageParam implements IVa... ... ... @Override public String toString() { return "SendMessageParam [isKeepVar=" + isKeepVar + ", ... .... + ", data=" + data + "]"; }
相等于:infoMap.toStirng();
就一个toStirng()方法,打开了潘多拉魔盒,当系统中该代码在第二天被运行到后,后台不停的抛出: java.lang.StackOverflowError 因为改动的东西不起眼,所以工程师3没有进行单元测试覆盖(属于搭本次项目顺风车的),却导致了本次项目的发布失败。 原因很简单:
日志打印时调用了String类的 public static String valueOf(Object obj) { return (obj == null) ? "null" : obj.toString(); } 方法。obj.toString()方法调用的是HashMap的toString()方法。HashMap继承了父类的toString方法:public class HashMap<K,V> extends AbstractMap<K,V> AbstractMap的toString方法:
public String toString() { Iterator<Entry<K,V>> i = entrySet().iterator(); if (! i.hasNext()) return "{}"; StringBuilder sb = new StringBuilder(); sb.append('{'); for (;;) { Entry<K,V> e = i.next(); K key = e.getKey(); V value = e.getValue(); sb.append(key == this ? "(this Map)" : key); sb.append('='); sb.append(value == this ? "(this Map)" : value); if (! i.hasNext()) return sb.append('}').toString(); sb.append(", "); } }
当Map中存在对象的时候:sb.append(value == this ? "(this Map)" : value);
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2011-03-10
简单的来说就是: Map map1 = new HashMap();
Map map2 = new HashMap(); map1.put("map", map2); map2.put("Map0", map1); System.out.println(map1); |
|
返回顶楼 | |
发表时间:2011-03-10
题目有点大啊
只是发布失败,项目没有以失败告终吧 |
|
返回顶楼 | |
发表时间:2011-03-10
最后修改:2011-03-10
llyzq 写道 题目有点大啊
只是发布失败,项目没有以失败告终吧 在预定的时间,没有达到预期的效果,就是失败,何况还产生了项目回滚,至少对现在来说它失败了 |
|
返回顶楼 | |
发表时间:2011-03-10
自引用并不会导致内存泄漏,现代jvm的gc算法都能处理这种循环引用的问题了。一个打印语句暴露隐藏的bug,还是很划算。
|
|
返回顶楼 | |
发表时间:2011-03-10
dennis_zane 写道 自引用并不会导致内存泄漏,现代jvm的gc算法都能处理这种循环引用的问题了。一个打印语句暴露隐藏的bug,还是很划算。
我已经说明了 或者代码片段1所在的线程完成任务后Map自引用失去了引用链而被垃圾回收。 当jvm发现自引用Map不存在和根的引用链的时候 |
|
返回顶楼 | |
发表时间:2011-03-10
chenyongxin 写道
dennis_zane 写道
自引用并不会导致内存泄漏,现代jvm的gc算法都能处理这种循环引用的问题了。一个打印语句暴露隐藏的bug,还是很划算。
我已经说明了 或者代码片段1所在的线程完成任务后Map自引用失去了引用链而被垃圾回收。 当jvm发现自引用Map不存在和根的引用链的时候
|
|
返回顶楼 | |
发表时间:2011-03-10
dennis_zane 写道
chenyongxin 写道
dennis_zane 写道
自引用并不会导致内存泄漏,现代jvm的gc算法都能处理这种循环引用的问题了。一个打印语句暴露隐藏的bug,还是很划算。
我已经说明了 或者代码片段1所在的线程完成任务后Map自引用失去了引用链而被垃圾回收。 当jvm发现自引用Map不存在和根的引用链的时候
类A被类B强引用,只有类B被回收后才会回收类A |
|
返回顶楼 | |
发表时间:2011-03-10
chenyongxin 写道
dennis_zane 写道
chenyongxin 写道
dennis_zane 写道
自引用并不会导致内存泄漏,现代jvm的gc算法都能处理这种循环引用的问题了。一个打印语句暴露隐藏的bug,还是很划算。
我已经说明了 或者代码片段1所在的线程完成任务后Map自引用失去了引用链而被垃圾回收。 当jvm发现自引用Map不存在和根的引用链的时候
类A被类B强引用,只有类B被回收后才会回收类A
我没有说你后半句的理解错误,问题在于你下面这句话给人误导: 代码运行近半年没有问题,虽然Map自引用会导致垃圾难以回收,但是因为基本每天都有项目或者小需求发布虚拟机重启,内存溢出没有暴露出来;或者代码片段1所在的线程完成任务后Map自引用失去了引用链而被垃圾回收。
我想说的是:自引用并不会导致垃圾难以回收,哪怕你不经常重启,内存泄漏也不一定会暴露出来。该怎么回收还是怎么回收,该内存泄漏还是内存泄漏,没有必然联系。
|
|
返回顶楼 | |
发表时间:2011-03-10
dennis_zane 写道
chenyongxin 写道
dennis_zane 写道
chenyongxin 写道
dennis_zane 写道
自引用并不会导致内存泄漏,现代jvm的gc算法都能处理这种循环引用的问题了。一个打印语句暴露隐藏的bug,还是很划算。
我已经说明了 或者代码片段1所在的线程完成任务后Map自引用失去了引用链而被垃圾回收。 当jvm发现自引用Map不存在和根的引用链的时候
类A被类B强引用,只有类B被回收后才会回收类A
我没有说你后半句的理解错误,问题在于你下面这句话给人误导: 代码运行近半年没有问题,虽然Map自引用会导致垃圾难以回收,但是因为基本每天都有项目或者小需求发布虚拟机重启,内存溢出没有暴露出来;或者代码片段1所在的线程完成任务后Map自引用失去了引用链而被垃圾回收。
我想说的是:自引用并不会导致垃圾难以回收,哪怕你不经常重启,内存泄漏也不一定会暴露出来。该怎么回收还是怎么回收,该内存泄漏还是内存泄漏,没有必然联系。
哦 呵呵,是我没有写清楚,我想表达的意思是因为虚拟机会经常重启,Map自引用就算没有被垃圾回收(存在和根的引用链的情况下),因为它占的内存比较小,不是很容易暴露内存溢出。 |
|
返回顶楼 | |