2\ 使用top -H -p PID 命令查看对应进程里的哪个线程占用CPU过高,取该线程pid
3\ 将线程的pid 转成16进制
4\jstack [进程pid]|grep -A 100 [线程pid的16进制] dump出jvm该线程的后100行,或者整个输出到文件
jstack -l pid > xxxfile
案例分析
现象:应用发布后,过二十分钟后load突然上升,居高不下. dump内存后没发现有内存泄漏,初步怀疑有线程在不断执行退不出.
验证:根据上面的方法找出占用cpu最高的java线程,dump出线程的后100行发现:
- 20881-thread-200" daemon prio=10 tid=0x0000000046edb800 nid=0x3bbb runnable [0x0000000044ad6000]
- java.lang.Thread.State: RUNNABLE
- at com.ibatis.sqlmap.engine.execution.SqlExecutor.getFirstResultSet(SqlExecutor.java:341)
- at com.ibatis.sqlmap.engine.execution.SqlExecutor.handleMultipleResults(SqlExecutor.java:299)
- at com.ibatis.sqlmap.engine.execution.SqlExecutor.executeQuery(SqlExecutor.java:190)
- at com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.sqlExecuteQuery(GeneralStatement.java:205)
- at com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.executeQueryWithCallback(GeneralStatement.java:173)
- at com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.executeQueryForObject(GeneralStatement.java:104)
- at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.queryForObject(SqlMapExecutorDelegate.java:566)
- at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.queryForObject(SqlMapExecutorDelegate.java:541)
- at com.ibatis.sqlmap.engine.impl.SqlMapSessionImpl.queryForObject(SqlMapSessionImpl.java:106)
- at org.springframework.orm.ibatis.SqlMapClientTemplate$1.doInSqlMapClient(SqlMapClientTemplate.java:273)
- at org.springframework.orm.ibatis.SqlMapClientTemplate.execute(SqlMapClientTemplate.java:209)
- at org.springframework.orm.ibatis.SqlMapClientTemplate.queryForObject(SqlMapClientTemplate.java:271)
可以看到SqlExecutor.getFirstResultSet在这个地方一直处于runnable.打开源码如下:
- while (hasMoreResults) {
- rs = stmt.getResultSet();
- if (rs != null) {
- break;
- }
- hasMoreResults = moveToNextResultsIfPresent(stmt);
- }
debug后发现在这个地方一直死循环
原因:review dao代码时发现一条update的操作是用selectForObject来跑的,接手过来的历史代码伤不起啊,以前跑在oracle的时候不会出现这个问题,这次去o换成mysql后这个隐藏的雷终于爆发了.把相应的select改成update问题解决.
额外注意:问题能在测试环节发现,就不是问题,关键在于测试环节测试人员一直没发现这个问题,到线上一下子就出来了.原因有两个,一是测试环境走不到这个分支,也就是测试用例不充分;二是测试环境操作有限,死几个线程问题不大,因为测试环境设置的超时时间比较短,导致问题没有明显暴露出来,到线上一下子就出来了.