前言
线上经常会遇到各种各样的问题,CPU 飚高,内存溢出,频繁 GC 。总的来说,基本上就是cpu、磁盘、内存以及网络等相关问题,所以进行排查时候尽量四个方面依次排查一遍。同时例如jstack、jmap等工具也是不囿于一个方面的问题的,基本上出问题就是df、free、top 三连,然后依次jstack、jmap伺候,具体问题具体分析即可。
示准JDK工县和实用程序
- 基本工具 (appletviewer, extcheck, jar, java, javac, javadoc, javah, javap, jdb, jdeps)
- 安全工具 (keytool,jarsigner, policytool, kinit, klist, ktab)
- 国际化工具 (native2ascii)
- 远程方法调用 (RMI) 工具 (rmic,rmiregistry,rmid,serialver)
- Java IDL和RMI-IIOP工具 (tnameserv,idlj,orbd,servertool)
- Java部署工具(avapackager,pack200,unpack200)
- Java Web Start具 (javaws)
- Java故障排除,性能分析,监视和管理工具 (jcmd,jconsole,jmc,jvisualvm)
- Java Web服务工具 (schemagen,wsgen,wsimport,xjc)
实验性JDK工具和实用程序
- 监视工具 (jps,jstat,jstatd)
- 故障排除工具 (info,jhat,jmap,jsadebugd,jstack)
- 脚本工具 (jrunscript)
故障排除文档
本文的排查环境是 Linux
CPU 飚高
思路:首先找到 CPU 飚高的那个 Java 进程,因为你的服务器会有多个 JVM 进程。然后找到那个进程中的 “问题线程”,最后根据线程堆栈信息找到问题代码。最后对代码进行排查。
- 通过 top 命令找到 CPU 消耗最高的进程,并记住进程 PID
- 再次通过 top -Hp [进程 PID] 找到 CPU 消耗最高的线程 ID,并记住线程 ID
- 由于此时的线程 PID 是十进制的,而堆栈信息中的线程 PID 是 16 进制的,因此我们需要将 10 进制的转换成 16 进制的,并用这个线程 PID 在堆栈中查找,使用printf ‘%x\n’ pid,可以将 10 进制转换成 16 进制,得到nid
- 通过 JDK 提供的 jstack 工具 jstack pid |grep ‘nid’ -C5 –color找到相应的堆栈信息,接着只要仔细分析一番即可。通常我们会比较关注WAITING和TIMED_WAITING的部分,BLOCKED就不用说了。我们可以使用命令cat jstack.log | grep “java.lang.Thread.State” | sort -nr | uniq -c来对jstack的状态有一个整体的把握,如果WAITING之类的特别多,那么多半是有问题
- 可以使用jstat -gc pid 1000命令来对gc分代变化情况进行观察
。1000表示采样间隔(ms)
。S0C/S1C、S0U/S1U、EC/EU、OC/OUMC/MU分别代表两个Survivor区、Eden区、老年代、元数据区的容量 和使用
。YGC/YGT、FGC/FGCT、GCT则代表YoungGc、FullGc的耗时和次数以及总耗时
内存问题排查
内存问题排查起来就稍嫌麻烦,场景也比较多。主要包括OOM、GC问题和堆外内存。一般来讲,我们会先用free命令先来检查一发内存的各种情况。
堆内内存
内存问题大多还都是堆内内存问题。表象上主要分为OutOfMemory(OOM)和StackOverflow(SOF)。
OOM
JMV中的内存不足,OOM大致可以分为以下几种:
-
Exception in thread “main” java.lang.OutOfMemoryError: unable to create new native thread
这个意思是没有足够的内存空间给线程分配java栈,基本上还是线程池代码写的有问题,比如说忘记shutdown,所以说应该首先从代码层面来寻找问题,使用jstack或者jmap。如果一切都正常,JVM方面可以通过指定Xss来减少单个thread stack的大小。另外也可以在系统层面,可以通过修改/etc/security/limits.confnofile和nproc来增大os对线程的限制 -
Exception in thread “main” java.lang.OutOfMemoryError: Java heap space
这个意思是堆的内存占用已经达到-Xmx设置的最大值,应该是最常见的OOM错误了。解决思路仍然是先应该在代码中找,怀疑存在内存泄漏,通过jstack和jmap去定位问题。如果说一切都正常,才需要通过调整Xmx的值来扩大内存。 -
Caused by: java.lang.OutOfMemoryError: Meta space
这个意思是元数据区的内存占用已经达到XX:MaxMetaspaceSize设置的最大值,排查思路和上面的一致,参数方面可以通过XX:MaxPermSize来进行调整(这里就不说1.8以前的永久代了)。
SOF
栈内存溢出,这个大家见到也比较多。
- Exception in thread “main” java.lang.StackOverflowError
表示线程栈需要的内存大于Xss值,同样也是先进行排查,参数方面通过Xss来调整,但调整的太大可能又会引起OOM。
使用JMAP定位代码内存泄漏
上述关于OOM和StackOverflow的代码排查方面,我们一般使用JMAP jmap -dump:format=b,file=filename pid来导出dump文件
另一方面,我们可以在启动参数中指定>-XX:+HeapDumpOnOutOfMemoryError来保存OOM时的dump文件