携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情
jps:虚拟机进程状况工具
可以列出正在运行的虚拟机进程,显示虚拟机执行主类名称以及进程的本地虚拟机唯一 ID。
命令格式:
jps [选项] [主机名]
主要选项:
| 选项 | 作用 |
|---|---|
| -q | 只输出进程 ID |
| -m | 输出虚拟机进程启动时传递给 main() 方法的参数 |
| -l | 输出主类的全名,如果进程执行的是 jar 包,那么输出其路径 |
| -v | 输出虚拟机进程启动时的 JVM 参数 |
jstat:虚拟机统计信息监视工具
可以监控虚拟机各种运行状态信息,如虚拟机进程中的类加载、内存、垃圾收集、即时编译等运行时数据。
命令格式:
jstat [选项 进程ID [ 查询间隔[秒|毫秒(默认)] [查询次数] ] ]
主要选项:
| 选项 | 作用 |
|---|---|
| -class | 监视类加载、卸载数量、总空间以及类装载所耗费的时间 |
| -gc | 监视 Java 堆状况,包括 Eden、Survivor、老年代、元空间等的容量,已用空间和垃圾收集时间合计等信息 |
| -gccapacity | 与 -gc 基本相同,主要关注堆中各个区域使用到的最大、最小空间 |
| -gcutil | 与 -gc 基本相同,主要关注已用空间占总空间的百分比 |
| -gccause | 与 -gcutil 一样,额外输出上次垃圾收集的原因 |
| -gcnew | 监视新生代垃圾收集情况 |
| -gcnewcapacity | 与 -gcnew 基本相同,主要关注使用到的最大、最小空间 |
| -gcold | 监视新生代垃圾收集情况 |
| -gcoldcapacity | 与 -gcold 基本相同,主要关注使用到的最大、最小空间 |
| -compiler | 输出即时编译器编译过的方法、耗时等信息 |
| -printcompilation | 输出已经被即时编译的方法 |
jinfo:Java 配置信息工具
可以实时查看和调整虚拟机各项参数(即 VM 参数)。
命令格式:
jinfo [选项] pid
windows 只提供了 -flag 选项
示例:
jinfo -flag xxxx 1234;
jmap:Java 内存映像工具
jmap 命令用于生成堆存储快照,可以用来查询 finalize 执行队列、Java 堆和方法区的详细信息(空间使用率、使用哪种收集器等)。
命令格式:
jmap [选项] 进程ID
应用示例
jmap -dump:format=b,file=IDEA.bin 11111
windows 只能使用部分选项,主要选项如下:
| 选项 | 作用 |
|---|---|
| -dump | 生成 Java 堆存储快照 格式为 -dump:[live,]format=b,file=<文件名>live 子参数说明是否只转储存活的对象 |
| -histo | 显示堆中对象统计信息,包括类、实例数量、合计容量 |
jhat:虚拟机堆快照分析工具
与 jmap 搭配使用,分析 jmap 生成的堆转储快照。
实际情况中应用较少,一般使用 VisualVM,Eclipse Memory Analyzer,IBM HeapAnalyzer 等工具分析快照文件。
命令格式:
jhat <快照文件路径>
执行完毕后访问localhost:7000查看分析结果。
jstack:Java 堆栈跟踪工具
可以生成当前虚拟机当前时刻的线程快照。
线程快照即每一条线程正在执行的方法堆栈的集合,如果线程死循环、死锁,都可以使用该工具检查。
命令格式:
jstack [选项] 进程ID
主要选项:
| 选项 | 作用 |
|---|---|
| -F | 当正常输出的请求不被响应时,强制输出线程堆栈 |
| -l | 显示关于锁的附加信息 |
| -m | 如果有使用到本地方法,可以显示本地方法栈 |
jconsole:Java监视与管理控制台
可视化工具,可以查看堆内存、虚拟机栈的使用情况。
使用:
jconsole 启动即可。
启动界面:
选择对应的 java 进程即可以查看详细的信息,可以查看堆内存使用情况,排查线程是否出现死锁等等。
代码示例
我们使用一份测试代码使用上面提到的工具,代码的主体功能为:
- 两个线程争抢锁,发生死锁现象
- 不断创建对象,促使堆内存内存紧张,触发垃圾回收
VM 参数说明:
- -Xmx20m -Xms20m:指定堆内存的最大大小和初始大小为 20m
- -Xmn10m:指定新生代内存为 10m
- -XX:+PrintGCDetails -verbose:gc 触发垃圾回收时,打印详情
// VM 参数 -Xmx20m -Xms20m -Xmn10m -XX:+PrintGCDetails -verbose:gc
public class TestTools {
private static MyLock lockA = new MyLock();
private static MyLock lockB = new MyLock();
public static void main(String[] args) {
testStack();
testHeap();
}
// 测试线程死锁,使用工具排查
private static void testStack() {
new Thread(()->{
synchronized (lockA) {
System.out.println(Thread.currentThread().getName() + "拿到锁A");
try {
Thread.sleep(5000);
synchronized (lockB) {
System.out.println(Thread.currentThread().getName() + "拿到锁B");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(()->{
synchronized (lockB) {
System.out.println(Thread.currentThread().getName() + "拿到锁B");
synchronized (lockA) {
System.out.println(Thread.currentThread().getName() + "拿到锁A");
}
}
}).start();
}
// 测试堆内存回收,使用工具查看内存占用情况
private static void testHeap() {
try {
System.in.read();
System.out.println("测试堆内存");
while (true) {
byte[] bytes = new byte[1024];
Thread.sleep(1);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyLock {
}
测试步骤:
- 启动程序,使用 jps 指令查看 Java 进程的进程 ID
jps
100336
61556 TestTools
67148 Launcher
95516 Jps
- 使用 jstat 查看堆内存占用情况
jstat -gcutil 61556
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 0.00 62.95 0.00 17.38 19.94 0 0.000 0 0.000 0.000
# 分别表示 from survivor, to survivor, eden, 老年代, 元空间, 压缩使用比例, Minnor GC回收次数, Minnor GC回收总耗时, FullGC 次数, FullGC回收总耗时, 所有GC总耗时
- 使用 jinfo 命令查看相关参数是否启用
jinfo -flag PrintGCDetails 61556
-XX:+PrintGCDetails # + 表示已经启用
# 如果命令为 jinfo 61556 ,终端会输出更多的参数信息,如下,包括 VM 版本号,jDK 版本等等
Attaching to process ID 61556, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.321-b07
Java System Properties:
java.runtime.name = Java(TM) SE Runtime Environment
java.vm.version = 25.321-b07
sun.boot.library.path = D:\program\Java\jdk1.8.0_321\jre\bin
java.vendor.url = http://java.oracle.com/
java.vm.vendor = Oracle Corporation
path.separator = ;
file.encoding.pkg = sun.io
java.vm.name = Java HotSpot(TM) 64-Bit Server VM
sun.os.patch.level =
sun.java.launcher = SUN_STANDARD
user.script =
user.country = CN
.......
- 使用 jmap 转储堆内存
jmap -dump:format=b,file=TestTools.bin 61556
Dumping heap to E:\Program\Java\TestDemo\TestTools.bin ...
Heap dump file created
- 使用 jhat 分析堆内存映像
jhat TestTools.bin
Dumping heap to E:\Program\Java\TestDemo\TestTools.bin ...
Heap dump file created
PS E:\Program\Java\TestDemo> jhat TestTools.bin
Reading from TestTools.bin...
Dump file created Sun Aug 07 12:09:21 CST 2022
Snapshot read, resolving...
Resolving 47141 objects...
Chasing references, expect 9 dots.........
Eliminating duplicate references.........
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.
访问 http://localhost:7000 查看
- 使用 jstack 工具
jstack -l 61556
2022-08-07 12:12:08
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.321-b07 mixed mode):
## 打印出很多信息,我们直接查看信息最后部分,有产生死锁的位置和原因,可以很快地排查到是源代码的第几行
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x00000258fd3d07a8 (object 0x00000000ff7aa638, a MyLock),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x00000258fd3cf258 (object 0x00000000ff7aa648, a MyLock),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at TestTools.lambda$testStack$1(TestTools.java:42) ## 问题出现在源代码第 42 行 和 31 行
- waiting to lock <0x00000000ff7aa638> (a MyLock)
- locked <0x00000000ff7aa648> (a MyLock)
at TestTools$$Lambda$2/1078694789.run(Unknown Source)
at java.lang.Thread.run(Thread.java:750)
"Thread-0":
at TestTools.lambda$testStack$0(TestTools.java:31)
- waiting to lock <0x00000000ff7aa648> (a MyLock)
- locked <0x00000000ff7aa638> (a MyLock)
at TestTools$$Lambda$1/1324119927.run(Unknown Source)
at java.lang.Thread.run(Thread.java:750)
Found 1 deadlock.
- 使用 jconsole 工具查看
启用工具后,选择对应的进程,并让 testHeap() 继续运行,查看内存使用情况
可以看到后面发生了一次 GC 后内存占用减少了
现在排查死锁,点击【线程】标签页,再点击检测死锁:
同样可以排查死锁。
小结
Java 提供了多种虚拟机工具帮助我们查看虚拟机的运行状况,在出现问题时可以有效地排查问题所在,在远程连接服务器的情况下,我们可以使用命令行工具排查问题,但多数时候,我们可以使用可视化工具来排查,例如 jconsole 可以更高效、方便地查看情况。
此外,还有其他的工具可以使用,例如 Eclipse 的 Eclipse Memory Analyzer 可视化工具。