JVM故障诊断
性能诊断
JDK 命令行工具
JDK 命令行工具位于 JDK 安装目录下的 bin 中,可以使用他们获取 Java 程序的运行状态等信息;了解即可,因为使用可视化性能监控工具也可以实现他们的功能。
jps
列出 Java 的进程列表。
# 列出Java进程以及入口类名称 jps # 忽略入口类名称 jps -q # -m输出入口函数参数;-l输出入口函数完整路径 jps -m -l # 显示传递给虚拟机的参数 jps -vjstat
观察 Java 程序运行时的相关信息。
# 输出 Java 进程 1234 的 ClassLoader 相关信息,每秒一次,共2次 jstat -class -t 1234 1000 2 # 输出 GC 相关的堆信息 jstat -gc 1234 # 查看堆中各个代的大小信息 jstat -gccapacity 1234 # 查看新生代各个区大小信息 jstat -gcnewcapacity 1234 # 查看老年代容量信息 jstat -gcoldcapacity 1234 # 查看永久区容量信息 jstat -gcpermcapacity 1234jmap
导出堆信息到文件。jcmd 更加强大一点(JDK1.7+新增),可以代替 jmap,详见下节。
# 统计Java程序的对象实例数量到s.txt文件 jmap -histo 1234 >/usr/local/s.txt # 获取当前Java程序的堆快照信息,format=b表示转储文件格式为二进制(binary) # .hprof文件可通过多种工具分析,如Visual VM、jhat等 jmap -dump:format=b,file=/usr/local/s.hprof 1234jcmd
多功能命令行工具,可用来查看堆、Java 进程、线程、GC 等信息。
# 列出当前系统的所有Java进程 jcmd -l # 列出针对进指定程所支持的指令 jcmd 1234 help # 在列出的指令中,可以随便找几个玩玩 jcmd 1234 VM.uptime # 查看虚拟机启动时间 jcmd 1234 Thread.print # 打印线程栈信息 jcmd 1234 GC.class_histogram # 查看系统中类的统计信息 jcmd 1234 GC.heap_dump /usr/d.dump # 导出堆信息,.dump文件可多种工具进行分析,如Visual VM jcmd 1234 VM.system_properties # 获取系统属性 jcmd 1234 VM.flags # 获取JVM启动参数jhat
jhat (在 JDk9+已移除,推荐使用 Visual VM)命令可以分析堆快照文件,并启动一个 http 服务器,可通过浏览器查阅。
# 分析堆文件,并提供一个web网页查阅 127.0.0.1:7000 jhat /usr/local/s.hprofjstack
输出 Java 程序的堆栈快照信息。可用于定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。
# 查看Java程序堆栈信息 jstack -l 1234 # 输出信息到.txt文件 jstack -l 1234 >/usr/local/info.txt提示
jstack 输出示例说明:
# jstack 输出示例 "main" #1 [14384] prio=5 os_prio=0 cpu=8656.25ms elapsed=16.49s tid=0x000001afd0853a20 nid=14384 runnable [0x0000001557efe000] java.lang.Thread.State: RUNNABLE at test.Main.main(Main.java:9) ... # 若出现死锁,会打印死锁信息 Found one Java-level deadlock: ...- "main" #1 [14384],"main"为线程名称,#1 是 Java 线程 ID,[14384]是操作系统分配的线程 ID。
- prio=5 os_prio=0,prio 为线程优先级,os_prio 为操作系统优先级。
- cpu=8656.25ms elapsed=16.49s,在 CPU 上执行的时间和线程已存在的时间。
- tid=xxx nid=14384,线程在 Java 虚拟机中的 ID 和系统分配给线程的 ID。
- runnable,线程状态,这里是可运行状态,表示该线程正在等待 CPU 资源执行。
- [0x0000001557efe000],线程堆栈起始地址。
- java.lang.Thread.State: RUNNABLE,线程的状态,与上面的 runnable 对应。
- at test.Main.main(Main.java:9),线程堆栈的一部分,指示了线程正在执行的代码位置,具体哪个类的哪个方法的第几行。
JDK 可视化性能监控工具
JConsole
JConsole 是 JDK 自带的图形化性能监控工具,操作很简单,可以自己玩玩儿。
Visual VM
Visual VM 需要单独安装,它可以替代 JDK 命令行工具,甚至替代 JConsole,是到目前为止随 JDK 发布的功能最强大的运行监视和故障处理程序。
第一次安装可能提示找不到 JDK 路径,需修改 /etc/visualvm.conf 指定 JDK 安装 路径。
远程连接
Visual VM 支持通过 JMX 远程连接 Java 程序,Java 程序需打开 JMX (Java Management Extensions,Java 管理扩展) 端口:
# 设置Java程序启动参数以便能被远程连接 java -Dcom.sun.management.jmxremote # 启用JMX远程管理 -Dcom.sun.management.jmxremote.port=8888 # 设置JMX端口 -Dcom.sun.management.jmxremote.ssl=false # 禁用SSL -Dcom.sun.management.jmxremote.authenticate=true # 启用认证 # 指定密码文件的路径,内容格式为username:password。 -Dcom.sun.management.jmxremote.password.file=/path/to/jmxremote.password -jar xxx.jar在需要远程监控的机器上,打开 Visual VM 的 File->Add JMX Connection...,输入连接信息即可。
异常定位
定位异常消耗 CPU 的类
若某个 Java 程序中存在某个大量占用 CPU 资源的类/方法,可以先使用 JDK 的jps指令定位 Java 程序的 PID(进程 ID),再结合第三方性能检测工具pidstat(一个功能强大的性能监测工具,Sysstat 的组件之一),得到该进程下线程信息,找到消耗资源的 TID(线程 ID),再使用 JDK 的jstack指令查询该线程 ID 对应的具体类和方法。
安装 pidstat
Ubuntusudo apt-get install sysstat sudo dpkg-reconfigure sysstat Select "Yes"CentOSsudo yum install sysstat sudo systemctl enable --now sysstat定位异常消耗 CPU 的类
定位进程 ID -> 定位该进程下某个异常线程 ID -> 根据异常线程 ID 定位与之对应的 Java 类/方法
# 定位 Java 程序 PID jps # 根据进程id查询cpu使用率;参数-u表示显示cpu相关参数,1 3 表示每秒采集一次,共采集三次 pidstat -p 1234 -u 1 3 # -t参数监控线程级别;可定位异常占用cpu的线程ID pidstat -p 1234 -u 1 3 -t # 使用 jstack 指令导出指定进程下所有线程信息,其中可以找到异常线程ID所对应的类和方法信息 jstack -l 1234 >/tmp/t.txt
定位异常消耗磁盘 IO 的类
使用 pidstat 工具也可以监控进程内线程的 I/O 情况。
# 定位 Java 程序 PID
jps
# 根据PID定位异常消耗磁盘的线程ID;-d 参数表示监控对象为磁盘I/O;-t 为线程级别的监控;
pidstat -p 1234 -d 1 3 -t
# 找到异常线程ID所对应的类和方法信息
jstack -l 1234 >/tmp/t.txt定位异常消耗内存的类
pidstat 工具还可以监控进程的内存使用情况。
# 定位 Java 程序 PID
jps
# 根据PID定位异常消耗磁盘的线程ID;-p 参数表示监控对象为内存;-t 为线程级别的监控;
pidstat -p 1234 -r 1 3 -t
# 找到异常线程ID所对应的类和方法信息
jstack -l 1234 >/tmp/t.txt内存溢出
内存溢出是指程序在申请内存时,没有足够的内存空间供其使用。
导致内存溢出(OutOfMemory,OOM)的常见原因包括堆溢出、直接内存溢出、永久区溢出、栈溢出、线程数量过多(每一个线程开启都会占用系统内存)、GC 低效等。
内存泄漏
内存泄漏是指程序在申请内存后,未能正确释放已申请的内存空间。
分析 Java 堆
前面介绍了通过 jmap、jcmd 能导出堆快照信息,但是原始文件查看不太方便,可以通过 Visual VM 菜单中的 Heap Dump 获取堆快照,可查看堆中的对象、线程、内存占用等信息,界面十分友好。
也可以通过Arthas工具查看堆信息(profiler 指令,提供一个 http 服务的网页),它是阿里 开源的 Java 诊断工具,能时查看应用 load、内存、gc、线程的状态信息。

