《诸子百家》发布后,发现游戏在有些情况下会崩溃。偶然崩溃。
由于我是负责主框架,网络协议和登录,数据等,所以这个修正bug的任务主要就落在我头上了。
一、bug
1.最头疼的bug
HEAP: Free Heap block xxxxxxxx modified at xxxxxxxx after it was freed
这个是最头疼的。知道崩溃了,知道内存地址,但是不知道在哪个文件。跟踪了好几天,打了很多日志,没头绪。后来网络上一找,也有很多人碰到这个问题。主要的解决方案是下载使PageHeap.EXE或GFlags.EXE检查内存越界错误,还有就是用WinDbg.
于是屁颠屁颠的把这些都下下来,搞了一阵子,发现不实用。主要是我们游戏里面,代码文件还是蛮多的,300多个文件,代码行估计有20万行~ 这个里找错误还真不好找,工具也没办法定位到具体的文件。
想了很久,后来觉得自己把问题想的太复杂了。
上面的错误很典型的就是内存已经被释放了,后面还继续使用。简单点说就是野指针!而造成野指针的原因很简单,就是内存被释放了。
于是,问题变简单了:找出所有释放内存的地方。
释放内存的,主要就2个:free和delete.
于是,我使用了2个宏,S_DEL(p) if(p){delete p; p=NULL;}
S_FREE(p) if(p){free(p); p=NULL;}
然后把所有用到这两个的地方都用宏替换掉,然后再把这两个宏里面真正释放的给注释掉。再跑,发现很OK,不会崩溃了。 于是,确认了是释放引起的。
然后在逐个把两个宏里的释放打开,发现打开S_FREE没问题,于是范围缩小到S_DEL里了。然后再把S_DEL里的释放也打开,再按照模块,把可以的地方用到的那个S_DEL注释掉~~ 这个样折腾了好久,最终找到了bug
2. 郁闷的bug
有时在团战的时候,发现打到一定阶段,程序就崩溃了。查看内存,看起来也没什么特殊的。。。
然后也是很长时间的排查。。。。。
二.具体总结
1. 经过很久的排查和使用上面的方法后,终于把这些崩溃点都解决了。主要有3个bug:
1).AMF3协议里面。我们使用独立的内存分配器进行内存的分配和释放,保证不内存泄漏。内存不泄露这个目标是达到了,但是有时把正在用的内存给释放掉了,造成了野指针。
为什么会把正在使用的内存给释放掉了呢?原来,在AMF3里,有一些数据是用引用的方式,而我们进行功能改写时,使用了同样的引用的方式,造成了多个节点使用同一个内存,因此导致其中某一个节点把这内存释放后,其他使用这个的都变成野指针了。
解决方案:不要使用引用,每个使用独立的内存
2)战斗里面,我们有一些战斗进度是用OnRender进行驱动的。比如A含有5个B,B含有10个C,我们的战斗进度的驱动之一是是C的OnRender()。而OnRender是一直循环调用的,用这进行驱动的时候,如果符合了某一个条件,比如战斗结束,那C通知B,B通知A,A就释放内存,通知结束。这种模式的问题是,A释放内存后,C返回到战斗结束的触发点继续运行就崩溃了~
解决方案:检测到战斗结束时,要所有的模块退出OnRender,然后才释放内存~
3)用户注销重新登录时,有时崩溃。
原因是用户注销时,我们会把所有的内存释放掉,把socket关闭。但是可能我们把内存释放掉时,由于socket还在,所以继续收到数据包进行AMF3的解析,而解析结果已经部分被清掉了,这个时候访问,野指针,崩溃~~
改成先把socket关闭,然后内存再释放掉。发现,有时还是会崩溃~~ 后来发现,因为我们的socket是单独线程处理的,也就是说我们释放内存和关闭sockt的操作是在2个线程里处理,这样总会出现一个先,一个后的,如果释放内存比关闭socket先执行,则还是会出现野指针。
解决方案:在socket线程里进行socket的释放,等socket释放完毕才进行内存的释放~