Java虚拟机问题排查

CPU 飚高

思路

首先找到 CPU 飚高的那个 Java 进程,因为你的服务器会有多个 JVM 进程,然后找到那个进程中的 “问题线程”,最后根据线程堆栈信息找到问题代码,最后对代码进行排查。

步骤

  • 通过 top 命令找到 CPU 消耗最高的进程,并记住进程 ID
  • 再次通过 top -Hp [进程 ID] 找到 CPU 消耗最高的线程 ID,并记住线程 ID.
  • 通过 JDK 提供的 jstack 工具 dump 线程堆栈信息到指定文件中。具体命令:jstack -l [进程 ID] >jstack.log

由于刚刚的线程 ID 是十进制的,而堆栈信息中的线程 ID 是16进制的,因此我们需要将10进制的转换成16进制的,并用这个线程 ID 在堆栈中查找。使用 printf “%x\n” [十进制数字] ,可以将10进制转换成16进制

  • 通过刚刚转换的16进制数字从堆栈信息里找到对应的线程堆栈,就可以从该堆栈中看出端倪

可能的情况

  • 某个业务死循环没有出口
  •  C2 编译器执行编译时抢占 CPU

当 Java 某一段代码执行次数超过10000次(默认)后,就会将该段代码从解释执行改为编译执行,也就是编译成机器码以提高速度。而这个 C2编译器就是做这个的。如何解决呢?项目上线后,可以先通过压测工具进行预热,这样,等用户真正访问的时候,C2编译器就不会干扰应用程序了。

  • GC 线程导致,那么极有可能是 Full GC ,那么就要进行 GC 的优化。

内存问题排查

思路

通常,内存的问题就是 GC 的问题,因为 Java 的内存由 GC 管理。有2种情况,一种是内存溢出了,一种是内存没有溢出,但 GC 不健康。

步骤

  • 内存溢出的情况可以通过加上 -XX:+HeapDumpOnOutOfMemoryError 参数,该参数作用是:在程序内存溢出时输出 dump 文件。
  • dump文件被常用的MAT,Jprofile,jvisualvm 等工具都可以分析,这些工具都能够看出到底是哪里溢出,哪里创建了大量的对象等等信息。

什么是健康的GC

一般来说,YGC 5秒一次左右,每次不超过50毫秒,FGC 最好没有,CMS GC 一天一次左右。

而 GC 的优化有2个维度,一是频率,二是时长。

YGC

YGC频率过低

如果 YGC 超过5秒一次,甚至更长,说明系统内存过大,应该缩小容量,

YGC频率过高

说明 Eden 区过小,可以将 Eden 区增大,但整个新生代的容量应该在堆的 30% - 40%之间,eden,from 和 to 的比例应该在 8:1:1左右,这个比例可根据对象晋升的大小进行调整。

YGC时间过长

YGC 有2个过程,一个是扫描,一个是复制,通常扫描速度很快,复制速度相比而言要慢一些

  • 如果每次都有大量对象要复制,就会将 STW 时间延长,
  • StringTable ,这个数据结构中存储着 String.intern 方法返回的常连池的引用,YGC 每次都会扫描这个数据结构(HashTable),如果这个数据结构很大,且没有经过 FGC,那么也会拉长 STW 时长
  • 操作系统的虚拟内存,当 GC 时正巧操作系统正在交换内存,也会拉长 STW 时长。

FGC

FGC 我们只能优化频率,无法优化时长,因为这个时长无法控制。

影响FGC频率的因素
  • Old 区内存不够
  • 元数据区内存不够
  • System.gc()
  • jmap 或者 jcmd工具导致
  • CMS Promotion failed 或者 concurrent mode failure
  • JVM 基于悲观策略认为这次 YGC 后 Old 区无法容纳晋升的对象,因此取消 YGC,提前 FGC
可能的情况
  •  Old 区内存不够导致 FGC
  • FGC之后还有大量对象,说明 Old 区过小,应该扩大 Old 区
  • 如果 FGC 后效果很好,说明 Old 区存在了大量短命的对象,优化的点应该是让这些对象在新生代就被 YGC 掉,通常的做法是增大新生代;如果有大而短命的对象,通过参数设置对象的大小,不要让这些对象进入 Old 区,还需要检查晋升年龄是否过小
  • 如果 YGC 后,有大量对象因为无法进入 Survivor 区从而提前晋升,这时应该增大 Survivor 区,但不宜太大。

工具

JDK提供了很多的工具,比如 jmap ,jcmd 等,oracle 官方推荐使用 jcmd 代替 jmap

jmap 可以打印对象的分布信息,可以 dump 文件,注意,jmap 和 jcmd dump 文件的时候会触发 FGC ,使用的时候注意场景。

jinfo,该工具可以查看当前 jvm 使用了哪些参数,并且也可以在不停机的情况下修改参数。

分析 dump 文件的可视化工具,MAT,Jprofile,jvisualvm 等,这些工具可以分析 jmap dump 下来的文件,看看哪个对象使用的内存较多。

线上环境要带上 GC 日志

参考

https://juejin.im/post/5db5a03bf265da4d3e173836#heading-0