1. 用top命令找到CPU占用率高的java进程pid,或者ps -aux|grep java查看所有java进程的pid。
2.假设出问题的进程pid是1000,用top -H -p 1000查看该进程下线程CPU占用率,或者用ps -mp 1000 -o THREAD,tid,time | sort -rn查看。
3.假设查得耗时线程ID为1002,将该线程ID转换为16进制格式:printf "%x\n" 1002,得到3ea,用jstack 1000 |grep 3ea -A 30查看java线程栈,找出问题代码。
在工作中碰到的一个案例:
如图top命令截图,某系统某服务器出现CPU占用率持续满载状态,pid为27839的java进程负载极高,双核CPU占用率近200%。
常规解决思路:
用ps -mp 27839 -o THREAD,tid| sort -rn查看该进程下线程CPU占用率(或者用top -H -p 27839查看)
显示结果首行如下:
USER %CPU PRI SCNT WCHAN USER SYSTEM TID
admin 192 19 - - - - 29736
查得耗时线程ID为29736,将该线程ID转换为16进制格式:printf "%x\n"29736,得到结果7428,用jstack27839|grep 7428 -A 30查看java线程堆栈信息:
"SofaBizProcessor-4-thread-28" prio=10 tid=0x00007f6d40067000 nid=0x7428 runnable [0x00007f6cebefb000]
java.lang.Thread.State: RUNNABLE
at java.util.HashMap.getEntry(HashMap.java:465)
at java.util.HashMap.get(HashMap.java:417)
at org.springframework.util.LinkedCaseInsensitiveMap.get(LinkedCaseInsensitiveMap.java:117)
atcom.ebao.ls.pa.service.impl.XinmeiEcifQueryPolicyServiceImpl.completePolicyCoverageInfo(XinmeiEcifQueryPolicyServiceImpl.java:240)
atcom.ebao.ls.pa.service.impl.XinmeiEcifQueryPolicyServiceImpl.queryBasicPolicyInfo(XinmeiEcifQueryPolicyServiceImpl.java:186)
atcom.ebao.ls.pa.service.impl.XinmeiEcifQueryPolicyServiceImpl.queryInfoByEcifid(XinmeiEcifQueryPolicyServiceImpl.java:90)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
可定位到问题代码为核心系统代码XinmeiEcifQueryPolicyServiceImpl.java:240
问题代码如下:
容易看出存在死循环。
另外还有其它的情况,比如在一个ETL项目中,线上系统执行一段时间就会发生 "java.lang.OutOfMemoryError: unable to create new native thread"。原因无疑是由于系统创建了大量线程而没有回收产生的内存溢出(具体原因可参考JVM中可生成的最大Thread数量)。这就不是某个线程导致的问题了,只能用jstack将JVM线程信息全部导出。通过观察日志发现该系统中存在大量jsch创建的运行态线程,通过代码发现项目中使用了jsch提供的sftp上传下载功能,有些地方没有调用关闭方法,导致线程没有正常结束。