今天服务器上跑的应用针对一个账号抛了异常,然而服务器并没有跟踪到异常从哪里抛出。像以往一样,我上服务器备份数据。由于数据量非常大,我是对指定的表进行备份的(结果疏忽了,漏了一张重要的表)。
恢复本地跑了一下,一切正常,可以正常打开页面。查看tomcat的日志记录,也没发现异常的stack trace.考虑到数据已经恢复了,本地测试正常,切换账号测试也是正常的,那就不可能有问题啊。
从日志中可以看到执行的方法已经缓存,说明方法已经成功执行了。但让我不解的是每次都会重新Cache一次,说明方法执行后的返回结果是null. 但如果拋了异常的话就不可能Cache啊。如果方法执行后的返回结果为null,在后面确实会抛出java.lang.nullpointerexception。
于是我看了看方法的返回值,确认了方法是不可能返回null的,如果没有数据,只会返回一个占存储空间的对象。到这里,我怀疑服务器是否内存不足,导致程序出了难以形容的bug。
于是我又看了一下服务器的资源占用情况,内存不足的这个猜测可能不对,但又不能否定。我觉得是jvm分配的内存太少导致。。。于是时间就这么耗掉了。
最后,我否定了之前的所有猜测,原因是既然只有这个账号出了问题,运行坏境出问题的可能性应该是非常低的。无奈之下,我写了个测试方法,在本地遍历所有账号的所有数据调用可能出问题的那个方法,期待的结果终于出来了,发现了一个bug:,我又重新备份了服务器上的数据,在本地测试了一下,果然抛出了同样异常。于是我知道了我之前少备份了一张表,进而被其他可能的因素套进去了。
还剩下两个问题,1. 服务器并没有跟踪到抛出异常的起点。2. 为什么那个方法没执行成功却能缓存结果。第一个问题stackoverflow上有人说是sun jvm优化的结果,把异常信息去掉了。要加入jvm参数 :-XX:-OmitStackTraceInFastThrow ,问题链接:https://stackoverflow.com/questions/2411487/nullpointerexception-in-java-with-no-stacktrace
,第二个问题我查了一下自己写的@Cache的实现,是放在finally中的,所以不管前面是否拋异常,都会执行。(真是一连串的坑啊)
总结:1. 如果我备份数据没遗漏,一下子就解决了这个问题,但现实总不可能每次都那么顺利。2. 遇到问题不要似懂非懂地解决,那样不是解决问题的办法。要对问题有十足的把握,知其然和所以然,不然不能放心。3. 尽可能多尝试测试的方法,也许会比较幸运。今天就是这样,如果所有账号每天的数据都是规律的,就不可能提前发现这个bug,我还得耗多一会吧。