从内存消耗状况来看,很明显系统存在内存泄漏情况,致使后期JVM不断FULL GC并导致CPU狂飙直至JVM崩溃,当时一看到这图,心里就咯噔一下shit,这么简单的应用居然有内存缺陷,太没面子了,后来仔细想了下觉得不太可能,这块代码没几个静态变量,Map、List的使用逻辑也是比较简单的,会不会是被JBOSS session对象撑爆的, 悟石同学用jmap dump出一份内存数据,MAT工具一分析,果然不出所料,有一个jboss session模样的对象占了内存的83%空间,哈哈,问题就这样轻松定位出来了,解决方案也很简单,因为该接口真实场景的调用走的HSF方式,不存在session,所以直接把JBOSS session改为1分钟,再进行测试,果然看到优美的锯齿状内存了如下图,TPS也在600左右趋于稳定, 迁移测试压力比较大,每20分钟full GC一次还是可以接受的,测试通过!
接下来进行事件运算接口的测试发现的问题就比较多了,解决起来也比较费事,不过当中分析的过程是相当有乐趣的,以下是测试细节:
刚开始测的时候以8个用户并发,TPS基本没问题,但有5%左右的数据测试结果报错,服务器偶尔还报空指针错误,测试好几遍发现概率都很一致,检查日志分析后发现主要报2种类型错误,一个是接口输入的日期参数跟预期不一致导致调用一个根据日期取数据的API返回空,另一个问题感觉从cache中命中对象失败导致系统报警,开始认为是准备数据的问题,但校验数据后未发现任何错误,于是怀疑什么地方并发考虑不周到,直接用单个用户压,发现错误少很多,但仍然有第二个错误。问题得一个一个解决,先查第一个,找到日期解析相关的代码片段找到如下:
.... public class EventContextParsor{ public final static SimpleDateFormat DEFAULT_DATA_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); … private RatableEvent parseRatableEvent(Element event){ … SimpleDateFormat sdf = DEFAULT_DATA_FORMAT; Date valueResult = sdf.parse(value); … } |
这里使用了静态的SimpleDateFormat本意是提升性能减小new开销,莫非 SimpleDateFormat线程不安全?写了个简单的单元测试一试,果然多线程并发的时候 “sdf.parse(value)”出来的结果面目全非,什么样的值都有,隐患啊,JDK文档也没有明确的指出,nnd! 解决方案,直接去掉静态变量,在调用函数里每次new SimpleDateFormat对象,测试后该问题解决。