JVM学习日记(十五)Day15——性能监控与调优(二)
好了我们这一篇继续来说命令行监控指令,上一篇说了4个比较重要的指令,其中用的比较多的也就是jstat和jmap了。
jhat:
堆转储分析工具
他是JDK自带的分析工具,分析我们上一篇说的jmap转存的内存快照,内置了一个微型的HTTP服务器提供Web界面浏览分析结果(默认端口是7000),帮助识别内存泄漏和大对象,同时支持OQL(Object Query Language)查询,但是已经在JDK9中被删除了,因为官方推荐使用VissualVM。
#基本语法
jhat [options] <heap-dump-file>#基本用法
# 分析堆转储文件并在7000端口启动服务
jhat heap.hprof# 访问分析结果
http://localhost:7000
参数 | 说明 | 示例 |
---|---|---|
-stack false | 关闭对象分配调用栈跟踪 | jhat -stack false heap.hprof |
-refs false | 关闭对象引用跟踪 | jhat -refs false heap.hprof |
-port <port> | 指定HTTP服务器端口(默认7000) | jhat -port 8000 heap.hprof |
-exclude <file> | 指定排除文件(包含排除的类名) | jhat -exclude exclude.txt heap.hprof |
-baseline <file> | 指定基准堆转储(用于比较) | jhat -baseline base.hprof heap.hprof |
-debug <int> | 设置调试级别(0-2) | jhat -debug 1 heap.hprof |
-version | 显示版本信息 | jhat -version |
-h /-help | 显示帮助信息 | jhat -help |
这个表示就已经启动成功了,可以去浏览器的http://localhost:8000/进行访问,这边建议小伙伴自己去尝试查看,知道这个东西就可以了,后面我们还是使用GUI图形界面去分析的,因为这个会更加的直观的。
jstack:生成 Java 进程的线程快照
我们上篇说到了jamp的堆快照,这里又有个可以查看线程的快照,因为程序出现卡顿不一定是堆堆问题嘛,也可能是线程的问题,jstack获取线程快照:显示所有 Java 线程的调用栈信息,诊断死锁:自动检测并报告死锁线程,分析高 CPU 占用:结合 top
或 jps
定位问题线程,排查线程阻塞:识别等待资源或锁的线程。
#基本语法
jstack [options] <pid>
参数 | 作用 | 示例 |
---|---|---|
-F | 强制生成线程转储(当进程挂起时使用) | jstack -F 1234 |
-l | 显示额外锁信息(包含同步器和 java.util.concurrent 锁) | jstack -l 1234 |
-m | 混合模式(显示 Java 和本地方法栈) | jstack -m 1234 |
-h /-help | 显示帮助信息 | jstack -help |
这里来说一下区别,和jmap不同的就是他不会要求生成文件然后再分析,而是直接把内容打印到控制台,当然也是可以存文件的,这个文件就是普通的txt文件不需要其他命令或者工具辅助查看了。
jstack -F 1234 > thread_dump.txt
#其实所有的输出都可以用这种方式存到指定的文件
#这个就是基本信息了
2025-08-01 17:13:34 //快照时间
Full thread dump Java HotSpot(TM) 64-Bit Server VM (21.0.5+9-LTS-239 mixed mode, emulated-client, sharing): //虚拟机信息Threads class SMR info: //一共30个线程,线程的地址如下
_java_thread_list=0x0000600002111ac0, length=30, elements={
0x0000000127010a00, 0x0000000127011200, 0x0000000127011a00, 0x000000011680f600,
0x000000011680d000, 0x000000011680d800, 0x000000011680aa00, 0x00000001268b2c00,
0x00000001269abc00, 0x0000000117102800, 0x0000000126b09200, 0x0000000127333c00,
0x0000000126b1c000, 0x000000012723ec00, 0x0000000117200e00, 0x0000000126b0e000,
0x0000000117008e00, 0x000000012734f000, 0x000000012734f800, 0x0000000127346200,
0x0000000127346a00, 0x000000011721b800, 0x0000000126b36c00, 0x0000000126b3ec00,
0x0000000126b3f400, 0x0000000126b3fc00, 0x0000000127347200, 0x0000000126b40400,
0x0000000127344600, 0x0000000126820000
}"Reference Handler" #9 [32259] daemon prio=10 os_prio=31 cpu=1.76ms elapsed=141885.62s tid=0x0000000127010a00 nid=32259 waiting on condition [0x000000016ec6e000]
#"Reference Handler":线程名称
#9:线程序号(JVM内部编号)
#[32259]:原生线程ID(Native Thread ID)
#daemon:这是一个守护线程(非用户线程)
#prio=10:Java线程优先级(1-10)
#os_prio=31:操作系统线程优先级
#cpu=1.76ms:该线程累计使用的CPU时间
#elapsed=141885.62s:线程已运行的时间(秒)
#tid=0x0000000127010a00:Java线程ID(内存地址)
#nid=32259:对应的操作系统线程ID(Native ID)
#waiting on condition:线程当前状态
#[0x000000016ec6e000]:线程栈的起始地址java.lang.Thread.State: RUNNABLE//运行状态at java.lang.ref.Reference.waitForReferencePendingList(java.base@21.0.5/Native Method)at java.lang.ref.Reference.processPendingReferences(java.base@21.0.5/Reference.java:246)at java.lang.ref.Reference$ReferenceHandler.run(java.base@21.0.5/Reference.java:208)
线程状态(Thread.State)
- RUNNABLE:线程正在执行或准备执行
- WAITING:线程等待某个条件(通常需要唤醒)
- BLOCKED:线程等待获取锁
- TIMED_WAITING:线程在指定时间内等待
这个就是jstack的示例,这里说一下什么是 daemon:守护线程(JVM退出时不等待这些线程)什么是非daemon:用户线程(JVM会等待这些线程结束),简单来说守护线程就是系统后台线程比如日志监控、垃圾回收等。
jcmd:多功能命令行工具
怎么说呢这哥们就是一个缝合怪,除了不能替代jstat其余的基本上都可以替代了,感觉前面讲的是不是有点多余了。
jcmd [options] <pid|main class> <command> [arguments]
部分 | 含义 | 是否必须 | 示例 |
---|---|---|---|
jcmd | 命令本身 | 必须 | jcmd |
[options] | 全局选项 | 可选 | -l , -f |
<pid|main class> | 目标进程标识 | 必须 | 1234 或 com.example.MyApp |
<command> | 要执行的诊断命令 | 可选 | Thread.print , GC.heap_dump |
[arguments] | 命令参数 | 可选 | filename=/tmp/dump.hprof |
命令 | 作用 | 示例 |
---|---|---|
VM.flags | 查看JVM参数 | jcmd 1234 VM.flags |
VM.system_properties | 查看系统属性 | jcmd 1234 VM.system_properties |
VM.uptime | 显示JVM运行时间 | jcmd 1234 VM.uptime |
VM.version | 显示JVM版本 | jcmd 1234 VM.version |
Thread.print | 生成线程转储 | jcmd 1234 Thread.print |
GC.class_histogram | 显示类直方图 | jcmd 1234 GC.class_histogram |
GC.heap_dump | 生成堆转储文件 | jcmd 1234 GC.heap_dump /path/to/dump.hprof |
GC.run_finalization | 强制执行finalize | jcmd 1234 GC.run_finalization |
GC.run | 显式触发GC | jcmd 1234 GC.run |
JFR.start | 开始JFR记录 | jcmd 1234 JFR.start name=myrec settings=profile |
JFR.dump | 导出JFR记录 | jcmd 1234 JFR.dump name=myrec filename=rec.jfr |
JFR.stop | 停止JFR记录 | jcmd 1234 JFR.stop name=myrec |
VM.native_memory | 本地内存统计 | jcmd 1234 VM.native_memory summary |
突然感觉这个命令有点复杂了有木有,主包这边就不演示了,这么多参数和命令又点头疼,具体使用什么命令还是要看小伙伴自己和使用的场景,有兴趣的小伙伴自己去试试看吧。
jstatd:远程监控工具
作用就是为远程主机提供 JVM 监控数据,通过 RMI 协议提供监控数据,可同时监控多个 Java 进程,支持 VisualVM、JConsole 等工具连接,这个咱们就简单说一下就好了,因为大多数生成环境是不允许的,多开一个端口都是有风险的。
#基本语法
jstatd [options]
#实例
jstatd -J-Djava.security.policy=jstatd.policy -p 3333
参数 | 作用 | 示例 |
---|---|---|
-p <port> | 指定 RMI 注册端口 | jstatd -p 1099 |
-n <name> | 指定 RMI 注册名称 | jstatd -n MyJstatd |
-J<option> | 传递参数给 JVM | jstatd -J-Djava.security.policy=my.policy |
-nr | 不创建 RMI 注册表 | jstatd -nr |
-r <port> | 指定 RMI 注册表端口 | jstatd -r 1099 |
-h /-help | 显示帮助信息 | jstatd -h |
总结
本篇把剩下的命令行已经全部介绍完了,下一篇讲讲GUI图形界面,大家好好练习命令吧。