最近又遇到一个奇怪的事情,一个非常简单的程序,就是web调用一下从数据库读取一批数据出来显示,程序刚开始一点问题没有,过一段时间就突然死掉了,所有的查询都没有反应。

探索+折磨:

    刚开始用的办法真的叫做非常原始。因为卡住连异常都不报,那很可能是什么没有设置超时。开始把web连接和数据库连接的超时设置检查并调整了一遍,但是问题依然没有解决。

    接着看觉得又像是数据库取不到链接,这可能是数据库连接池耗尽,那么应该连接都没有释放。到mysql中查当前连接数,非常正常,感觉也不是连接的问题。当程序卡住的时候,其他和数据库连接的程序都很正常,自己的程序一重启也是正常的。所以应该也不是连接数耗尽的问题。期间也把程序的当前线程打印出来数了一下,线程数也没有异常膨胀。

    把程序执行的每一句话前后都加上日志输出,惊讶的发现程序停在service调用dao的那个地方了。dao方法刚进去的第一句打印日志的话都不输出。难道是程序调用还能出错?!这个问题我现在也没想通,估计是spring从中搞的什么鬼。所以干脆就把dao层拆掉,代码都复制到service层来,结果代码卡在数据库查询那一句上了。

    查看CPU和内存消耗都很低,但程序无故停住,这个现象和死锁也是一样的。但是程序中根本没用到锁,所以也可以排除。

    想到自己是用dbcp的,上网一查说有bug的人很多,这就在想是不是考虑升级一下dbcp的版本,或者换成c3p0来试试。无奈中把数据库配置还弄错了,出问题的那个数据源反而没更换成c3p0,导致问题还是没解决。

醒悟:

    SB的事情做的太多了,居然傻到数线程数,为什么不直接查看程序停在哪一句呢?!当程序再次卡住的时候,果断kill -3 [pid]把内存状态打印出来,结果一看就停在dbcp的getConnection方法上了。显然我是遇到了传说中的dbcp的bug,这次更换的时候又发现上次配置文件给写错了,悲剧啊。把dbcp升级到最新版本,运行了几天都没再现问题了。

    kill命令只能在linux下面用,其实也可以用jdk自带的工具,比如用jstack去查看线程运行情况,这样在windows下面也可以使用。

总结:

    Java是一个很开放的程序,对于出问题的情况利用提供的工具大部分都很容易定位。这中间一是你要知道有什么样的工具可以辅助你,至少你要把jdk/bin下面自带的工具看一遍;另外就是要有借助工具解决问题的意识,如果还是和刚开始学写代码的时候就知道到处打印输出的语句,那解决问题的层次实在是太低了,也很难把问题真正解决。