天天看点

java进程 cpu load过高分析过程

1\  jps -v列出所有的java进程 , top找出cpu占用过高的对应的java 进程pid

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问题解决.

额外注意:问题能在测试环节发现,就不是问题,关键在于测试环节测试人员一直没发现这个问题,到线上一下子就出来了.原因有两个,一是测试环境走不到这个分支,也就是测试用例不充分;二是测试环境操作有限,死几个线程问题不大,因为测试环境设置的超时时间比较短,导致问题没有明显暴露出来,到线上一下子就出来了.

继续阅读