Java常用命令及性能调优工具
1、Linux命令
1.1 top命令
top命令的输出可以分为上下两部分:系统统计信息和进程统计信息。
系统统计信息:
- 第一行:任务队列信息。等同于uptime命令。
20:30:40 up 71 days, 21:36, 1 user, load average: 0.15, 0.16, 0.13
系统当前时间、系统运行时间、当前登录用户数。load average表示系统的平均负载(1分钟内、5分钟内、15分钟内)。
- 第二行:进程统计信息。分别为总的进程数、运行中的进程数、睡眠进程数、停止的进程数以及僵尸进程数。
Tasks: 75 total, 1 running, 74 sleeping, 0 stopped, 0 zombie
- 第三行:CPU统计信息。
Cpu(s): 0.4%us, 0.2%sy, 0.0%ni, 99.4%id, 0.1%wa, 0.0%hi, 0.0%si, 0.0%st
us:用户态CPU占用率、sy:内核态CPU占用率、ni:用户进程空间改变过优先级的进程CPU占用率、id:空闲CPU占用率、wa:等待输入输出的CPU时间百分比、hi:硬件中断请求、si:软件中断请求、st:CPU服务于软中断所耗费的时间总额。
- 第四行:内存统计信息。
Mem: 1922244k total, 1200496k used, 721748k free, 181508k buffers
依次表示物理内存总量、已使用内存量、空闲内存量、内核缓冲使用量。
- 第五行:交换区统计信息。
Swap: 0k total, 0k used, 0k free, 435204k cached
依次表示交换区总量、已使用交互区总量、空闲交换区总量、缓冲的交换区总量。
进程统计信息:
- PID:进程id
- USER:进程所有者的用户id
- PR:优先级
- NI:nice值。负值表示高优先级、正值表示低优先级
- VIRT:进程使用的虚拟内存总量。VIRT=SWAP+RES
- RES:进程使用的、未被换出的物理内存大小
- SHR:共享内存大小(KB)
- S:进程状态。R-运行、S-睡眠、D-不可中断的睡眠、T-跟踪/停止、Z-僵尸
- %CPU:上次更新到现在的CPU时间占用百分比
- %MEM:进程使用的物理内存百分比
- %TIME+:进程使用的CPU时间总计
- COMMAND:命令名
top常用子命令:
- h:帮助
- k:终止一个进程
- c:切换显示完整的命令行
- M:按照内存大小排序
- P:按照CPU占用百分比排序
1.2 sar命令
sar命令也是Linux系统中重要的性能检测工具之一,可周期性的对内存和CPU的使用情况进行采样。如下:
sar 1 5
表示每隔1秒采样1次,共采样5次。
Linux 2.6.32-573.22.1.el6.x86_64 (-) 03/14/2017 _x86_64_ (1 CPU)
02:40:35 PM CPU %user %nice %system %iowait %steal %idle
02:40:36 PM all 0.99 0.00 0.99 0.00 0.00 98.02
02:40:37 PM all 0.00 0.00 0.00 0.00 0.00 100.00
02:40:38 PM all 1.00 0.00 0.00 0.00 0.00 99.00
02:40:39 PM all 0.00 0.00 0.00 0.00 0.00 100.00
02:40:40 PM all 0.00 0.00 0.00 0.00 0.00 100.00
Average: all 0.40 0.00 0.20 0.00 0.00 99.40
option选项:
- -A:所有报告。
- -u:CPU利用率。默认选项。
- -d:磁盘使用情况。
- -b:I/O使用情况。
- -q:查看队列长度。
- -r:内存使用情况。
- -n:查看网络信息统计。【ALL】
- -o:将采样结果输出到文件
1.3 vmstat命令
vmstat命令同样也是Linux系统中重要的性能检测工具之一,不仅可周期性的对内存、CPU进行采样,还可以对swap使用进行采样。
vmstat 1 5
表示每隔1秒采样一次,共采样5次。
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 401032 183320 732700 0 0 3 2 3 6 0 0 99 0 0
0 0 0 401016 183320 732700 0 0 0 0 234 512 0 1 99 0 0
0 0 0 401016 183320 732700 0 0 0 0 220 495 0 0 100 0 0
0 0 0 401016 183320 732700 0 0 0 32 240 518 1 0 99 0 0
0 0 0 401016 183320 732700 0 0 0 0 244 511 0 0 100 0 0
- procs
- r:等待运行的进程数
- b:处在非中断睡眠状态的进程数
- memory
- swpd:虚拟内存使用情况(KB)
- free:空闲内存大小(KB)
- buff:表示块设备(block device)所占用的缓存页,包括:直接读写块设备、以及文件系统元数据(metadata),比如SuperBlock所使用的缓存页(KB)
- cache:表示普通文件数据所占用的缓存页
- swap
- si:从磁盘交换到内存的交换页数量(KB/秒)
- so:从内存交换到磁盘的交换页数量(KB/秒)
- io
- bi:发送到块设备的块数(块/秒)
- bo:从块设备接收到的块数(块/秒)
- system
- in:每秒中断数
- cs:每秒上下文切换次数
- cpu
- us:用户态CPU占用率
- sy:内核态CPU占用率
- id:空闲CPU占用率
-wa:等待输入输出的CPU时间百分比 - st:CPU服务于软中断所耗费的时间总额
1.4 iostat命令
iostat可以提供详尽的I/O信息。
iostat 1 2
表示每隔1秒采样1次,共采样2次。
Linux 2.6.32-573.22.1.el6.x86_64 (-) 03/14/2017 _x86_64_ (1 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
0.42 0.00 0.17 0.05 0.00 99.36
Device: tps Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn
vda 0.49 5.86 4.35 39755548 29531688
vdb 0.00 0.00 0.00 1392 0
avg-cpu: %user %nice %system %iowait %steal %idle
1.00 0.00 0.00 0.00 0.00 99.00
Device: tps Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn
vda 0.00 0.00 0.00 0 0
vdb 0.00 0.00 0.00 0 0
- tps:该设备每秒的传输次数
- Blk_read/s:每秒从设备读取的数据量
- Blk_wrtn/s:每秒向设备写入的数据量
- Blk_read:读取的总数据量
- Blk_wrtn:写入的总数据量
1.5 pidstat命令
pidstat也是一个功能强大的性能检测工具。不仅可以检测进程的性能,还可以检测线程的性能。
1.5.1 CPU使用率监控
下面的程序中,开启了3个线程,其中一个大量占用CPU,其他2个线程处于空闲状态。
public class CPUMain {
public static class BusyCPUTask implements Runnable {
@Override
public void run() {
while (true) {
double i = Math.random() * Math.random();
}
}
}
public static class LazyCPUTask implements Runnable {
@Override
public void run() {
try {
while (true) {
TimeUnit.SECONDS.sleep(1);
}
} catch (Exception e) {
// do nothing
}
}
}
public static void main(String[] args) {
new Thread(new BusyCPUTask()).start();
new Thread(new LazyCPUTask()).start();
new Thread(new LazyCPUTask()).start();
}
}
- 1、使用
jps -l
找到该Java程序的PID:
[aaa@qq.com ~]# jps -l
19077 sun.tools.jps.Jps
19064 command.CPUMain
17338 org.elasticsearch.bootstrap.Elasticsearch
17851 org.jruby.Main
- 2、使用
pidstat
命令输出该程序的CPU使用情况:
[root@- ~]# pidstat -p 19064 -u 1 3
Linux 2.6.32-573.22.1.el6.x86_64 (-) 03/14/2017 _x86_64_ (1 CPU)
05:49:01 PM PID %usr %system %guest %CPU CPU Command
05:49:02 PM 19064 99.00 0.00 0.00 99.00 0 java
05:49:03 PM 19064 100.00 0.00 0.00 100.00 0 java
05:49:04 PM 19064 99.00 0.00 0.00 99.00 0 java
Average: 19064 99.33 0.00 0.00 99.33 - java
-p 指定进程的ID,-u 对CPU使用率进行监控;每隔1秒采样1次,共采样3次。
- 3、使用-t
参数找出最占CPU的线程
[aaa@qq.com ~]# pidstat -p 19064 -u -t 1 1
Linux 2.6.32-573.22.1.el6.x86_64 (-) 03/14/2017 _x86_64_ (1 CPU)
05:56:00 PM TGID TID %usr %system %guest %CPU CPU Command
05:56:01 PM 19064 - 99.00 0.00 0.00 99.00 0 java
05:56:01 PM - 19064 0.00 0.00 0.00 0.00 0 |__java
05:56:01 PM - 19065 0.00 0.00 0.00 0.00 0 |__java
05:56:01 PM - 19066 0.00 0.00 0.00 0.00 0 |__java
05:56:01 PM - 19067 0.00 0.00 0.00 0.00 0 |__java
05:56:01 PM - 19068 0.00 0.00 0.00 0.00 0 |__java
05:56:01 PM - 19069 0.00 0.00 0.00 0.00 0 |__java
05:56:01 PM - 19070 0.00 0.00 0.00 0.00 0 |__java
05:56:01 PM - 19071 0.00 0.00 0.00 0.00 0 |__java
05:56:01 PM - 19072 0.00 0.00 0.00 0.00 0 |__java
05:56:01 PM - 19073 0.00 0.00 0.00 0.00 0 |__java
05:56:01 PM - 19074 100.00 0.00 0.00 100.00 0 |__java
05:56:01 PM - 19075 0.00 0.00 0.00 0.00 0 |__java
05:56:01 PM - 19076 0.00 0.00 0.00 0.00 0 |__java
... ...
可以看到, 19074 这个线程占用了大量CPU。
备注:上述2和3两个过程可以使用top -p 19064 -H
命令查出哪个线程占用大量CPU,如下:
[aaa@qq.com ~]# top -p 19064 -H
top - 17:57:41 up 78 days, 19:03, 3 users, load average: 1.00, 0.86, 0.55
Tasks: 13 total, 1 running, 12 sleeping, 0 stopped, 0 zombie
Cpu(s): 99.7%us, 0.3%sy, 0.0%ni, 0.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 1922244k total, 1584308k used, 337936k free, 185344k buffers
Swap: 0k total, 0k used, 0k free, 776028k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
19064 root 20 0 2414m 23m 11m S 0.0 1.3 0:00.00 java
19065 root 20 0 2414m 23m 11m S 0.0 1.3 0:00.07 java
19066 root 20 0 2414m 23m 11m S 0.0 1.3 0:00.00 java
19067 root 20 0 2414m 23m 11m S 0.0 1.3 0:00.00 java
19068 root 20 0 2414m 23m 11m S 0.0 1.3 0:00.00 java
19069 root 20 0 2414m 23m 11m S 0.0 1.3 0:00.00 java
19070 root 20 0 2414m 23m 11m S 0.0 1.3 0:00.00 java
19071 root 20 0 2414m 23m 11m S 0.0 1.3 0:00.00 java
19072 root 20 0 2414m 23m 11m S 0.0 1.3 0:00.00 java
19073 root 20 0 2414m 23m 11m S 0.0 1.3 0:00.12 java
19074 root 20 0 2414m 23m 11m R 99.6 1.3 9:09.31 java
19075 root 20 0 2414m 23m 11m S 0.0 1.3 0:00.01 java
19076 root 20 0 2414m 23m 11m S 0.0 1.3 0:00.00 java
得到的结果同样是19074 这个线程。
- 4、jstack定位线程栈 jstack 19064 > jstack.log
- 5、查看19074线程情况
通过使用命令printf %x 19074
得到19074的16进制:4a82
查看4a82线程:
"Thread-0" #8 prio=5 os_prio=0 tid=0x00007fae380fa800 nid=0x4a82 runnable [0x00007fae3c551000]
java.lang.Thread.State: RUNNABLE
at command.CPUMain$BusyCPUTask.run(CPUMain.java:14)
at java.lang.Thread.run(Thread.java:745)
结论:可以定位到线程在执行CPUMain.java:14
占用了大量CPU。
1.5.2 I/O监控
下面的程序中,开启了3个线程,其中一个大量占用I/O,其他2个线程处于空闲状态。
public class IOMain {
public static class BusyIOTask implements Runnable {
@Override
public void run() {
try {
FileOutputStream fos;
FileInputStream fis;
while (true) {
fos = new FileOutputStream(new File("temp"));
for (int i = 0; i < 1000; i++) {
fos.write(i); // 写操作
}
fos.close();
fis = new FileInputStream(new File("temp"));
while (fis.read() != -1); // 读操作
fis.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static class LazyIOTask implements Runnable {
@Override
public void run() {
try {
while (true) {
TimeUnit.SECONDS.sleep(1);
}
} catch (Exception e) {
// do nothing
}
}
}
public static void main(String[] args) {
new Thread(new IOMain.BusyIOTask()).start();
new Thread(new IOMain.LazyIOTask()).start();
new Thread(new IOMain.LazyIOTask()).start();
}
}
- 1、使用
jps -l
找到该Java程序的PID:
[aaa@qq.com ~]# jps -l
17338 org.elasticsearch.bootstrap.Elasticsearch
17851 org.jruby.Main
19323 sun.tools.jps.Jps
19310 command.IOMain
- 2、使用
pidstat
命令输出该程序的各个线程的I/O使用情况:
[aaa@qq.com ~]# pidstat -p 19310 -d -t 1 3
Linux 2.6.32-573.22.1.el6.x86_64 (-) 03/14/2017 _x86_64_ (1 CPU)
06:30:13 PM TGID TID kB_rd/s kB_wr/s kB_ccwr/s Command
06:30:14 PM 19310 - 0.00 3482.83 1741.41 java
06:30:14 PM - 19310 0.00 0.00 0.00 |__java
06:30:14 PM - 19311 0.00 0.00 0.00 |__java
06:30:14 PM - 19312 0.00 0.00 0.00 |__java
06:30:14 PM - 19313 0.00 0.00 0.00 |__java
06:30:14 PM - 19314 0.00 0.00 0.00 |__java
06:30:14 PM - 19315 0.00 0.00 0.00 |__java
06:30:14 PM - 19316 0.00 0.00 0.00 |__java
06:30:14 PM - 19317 0.00 0.00 0.00 |__java
06:30:14 PM - 19318 0.00 0.00 0.00 |__java
06:30:14 PM - 19319 0.00 0.00 0.00 |__java
06:30:14 PM - 19320 0.00 3478.79 1741.41 |__java
06:30:14 PM - 19321 0.00 0.00 0.00 |__java
06:30:14 PM - 19322 0.00 0.00 0.00 |__java
... ...
- 3、定位线程栈
同样地使用jstack
命令,导出该程序的线程栈,查找nid=19320(0x4b78)的线程,即可定位问题。
1.5.3 内存监控
使用pidstat
命令,还可以监视指定进程的内存的使用情况。
[aaa@qq.com test]# pidstat -p 17338 -r 1 3
Linux 2.6.32-573.22.1.el6.x86_64 (-) 03/14/2017 _x86_64_ (1 CPU)
06:36:36 PM PID minflt/s majflt/s VSZ RSS %MEM Command
06:36:37 PM 17338 0.00 0.00 2677708 280504 14.59 java
06:36:38 PM 17338 0.00 0.00 2677708 280504 14.59 java
06:36:39 PM 17338 0.00 0.00 2677708 280504 14.59 java
Average: 17338 0.00 0.00 2677708 280504 14.59 java
- minflt/s:该进程每秒minor faults(不需要从磁盘中调出内存页)的总数
- majflt/s:该进程每秒major faults(需要从磁盘中调出内存页)的总数
- VSZ:该进程使用的虚拟内存大小(KB)
- RSS:该进程使用的物理内存大小(KB)
- %MEM:内存占用率
2、JDK命令
2.1 jps命令
jps命令类似于Linux的ps,只不过ps是查看Linux系统的进程,jps只是列出当前用户启动的Java进程。
- jps
[aaa@qq.com ~]# jps
17338 Elasticsearch
17851 Main
21166 Jps
- jps -q:只输出进程id
- jps -m:输出传递给Java Main函数的参数
- jps -l:输出Main函数的完整路径
- jps -v:输出传递给JVM的参数
2.2 jstat命令
jstat命令用于观察Java程序的运行时工具。
Usage: jstat -help|-options
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
option可选项:
- -class:显示ClassLoader的相关信息
[aaa@qq.com ~]# jstat -class -t 17338
Timestamp Loaded Bytes Unloaded Bytes Time
4302040.9 8482 15664.4 12 12.0 7.94
Loaded表示载入类的数量、Bytes表示载入类的大小、Unloaded表示卸载类的数量、Bytes表示卸载类的大小、Time表示在加载及卸载类上所花的时间。
- -compiler:显示JIT编译的相关信息
[aaa@qq.com ~]# jstat -compiler -t 17338
Timestamp Compiled Failed Invalid Time FailedType FailedMethod
4312097.9 12812 0 0 73.32 0
Complied表示编译任务执行的次数、Failed表示编译失败的次数、Invalid表示不可用的次数、Time表示编译的总耗时、FailedType表示最后一次编译失败的类型、FailedMethod表示最后一次编译失败的类名和方法名。
- -gc:显示与GC相关的堆信息
[aaa@qq.com ~]# jstat -gc -t 17338
Timestamp S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
4312491.0 8512.0 8512.0 76.1 0.0 68160.0 15675.5 439104.0 18409.8 47780.0 47055.7 5600.0 5363.6 644 4.077 4 0.176 4.253
- S0C:S0(From)的大小(KB)
- S1C:S1(To)的大小(KB)
- S0U:S0(From)已使用的大小(KB)
- S1U:S1(To)已使用的大小(KB)
- EC:Eden区的大小(KB)
- EU:Eden已使用的大小(KB)
- OC:老年代大小(KB)
- OU:老年代已使用的大小(KB)
- MC:方法区大小(KB)
- MU:方法区已使用大小(KB)
- CCSC:压缩类空间大小(KB)
- CCSU:压缩类空间已使用大小(KB)
- PC:永久区大小(KB)
- PU:永久区已使用大小(KB)
- YGC:新生代GC次数
- YGCT:新生代GC耗时
- FGC:Full GC次数
- FGCT:Full GC耗时
-
GCT:GC总耗时
- -gccapacity:显示各个代的容量及使用情况
[aaa@qq.com ~]# jstat -gccapacity -t 17338
Timestamp NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC
4313185.9 85184.0 85184.0 85184.0 8512.0 8512.0 68160.0 439104.0 439104.0 439104.0 439104.0 0.0 1091584.0 47780.0 0.0 1048576.0 5600.0 644 4
与-gc
相比,不仅输出了各个代当前的大小,还输出了各个代的最小、最大值。
- NGCMN:新生代最小值(KB)
- NGCMX:新生代最大值(KB)
- NGC:当前新生代大小(KB)
- OGCMN:老年代最小值(KB)
- OGCMX:老年代最大值(KB)
- PGCMN:永久区最小值(KB)
- PGCMX:永久区最大值(KB)
- -gccause:显示垃圾收集相关信息(同gcutil),同时显示最后一次或当前正在发生的垃圾收集的诱发原因
[aaa@qq.com ~]# jstat -gccause -t 17338
Timestamp S0 S1 E O M CCS YGC YGCT FGC FGCT GCT LGCC GCC
4313692.2 0.89 0.00 43.07 4.19 98.48 95.78 644 4.077 4 0.176 4.253 Allocation Failure No GC
- LGCC:最近一次GC的的原因
-
GCC:当前GC的原因
- -gcnew:显示新生代信息
[aaa@qq.com ~]# jstat -gcnew -t 17338
Timestamp S0C S1C S0U S1U TT MTT DSS EC EU YGC YGCT
4314215.7 8512.0 8512.0 76.1 0.0 6 6 4256.0 68160.0 33355.0 644 4.077
- TT:新生代对象晋升到老年代对象的年龄
- MTT:新生代对象晋升到老年代对象的年龄最大值
-
DSS:所需的幸存区大小
- -gcnewcapacity:显示新生代容量及使用情况
[aaa@qq.com ~]# jstat -gcnewcapacity -t 17338
Timestamp NGCMN NGCMX NGC S0CMX S0C S1CMX S1C ECMX EC YGC FGC
4314357.1 85184.0 85184.0 85184.0 8512.0 8512.0 8512.0 8512.0 68160.0 68160.0 644 4
- S0CMX:S0区的最大值
- S1CMX:S1区的最大值
-
ECMX:Eden区的最大值
- -gcold:显示老年代和永久代的信息
[aaa@qq.com ~]# jstat -gcold -t 17338
Timestamp MC MU CCSC CCSU OC OU YGC FGC FGCT GCT
4314463.2 47780.0 47055.7 5600.0 5363.6 439104.0 18409.8 644 4 0.176 4.253
- -gcoldcapacity:显示老年代容量
[aaa@qq.com ~]# jstat -gcoldcapacity -t 17338
Timestamp OGCMN OGCMX OGC OC YGC FGC FGCT GCT
4314490.9 439104.0 439104.0 439104.0 439104.0 644 4 0.176 4.253
- -gcutil:显示垃圾收集信息
[aaa@qq.com ~]# jstat -gcutil -t 17338
Timestamp S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
4314594.9 0.89 0.00 52.34 4.19 98.48 95.78 644 4.077 4 0.176 4.253
- -printcompilation:输出HotSpot编译方法的统计
[aaa@qq.com ~]# jstat -printcompilation -t 17338
Timestamp Compiled Size Type Method
4314622.9 12827 173 1 org/elasticsearch/cluster/routing/allocation/decider/AllocationDeciders canRebalance
其他选项:
- -t:在输出信息前加入一个时间戳,显示程序的运行时间
- -h:在周期性数据输出时,输出多少行数据后,跟着输出一个表头信息
- interval:指定输出统计数据的周期(毫秒)
- count:指定一共输出多少次数据
2.3 jinfo命令
jinfo可以查看正在运行的Java程序的JVM参数,并且支持运行时修改个别参数。
jinfo [option] <pid>
option可选项:
- -flag to print the value of the named VM flag,打印指定JVM的参数值
- -flag [+|-] to enable or disable the named VM flag,设置指定JVM参数的boolean值
- -flag = to set the named VM flag to the given value,设置指定JVM参数值
- -flags to print VM flags,打印JVM的参数值
- -sysprops to print Java system properties,打印Java系统的属性信息
- to print both of the above,打印flags和syspros
2.4 jmap命令
jmap命令能生成Java应用程序的堆快照和对象的统计信息。
jmap -histo 17338 > jmap.log
num #instances #bytes class name
----------------------------------------------
1: 119627 16888576 [C
2: 74845 8336640 [B
3: 14991 3763272 [I
4: 66990 3215520 java.lang.management.MemoryUsage
5: 32973 2374056 java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask
6: 42965 2055352 [Ljava.lang.Object;
7: 81756 1962144 java.lang.String
8: 9035 994824 java.lang.Class
9: 6709 858752 sun.nio.fs.UnixFileAttributes
10: 24718 790976 sun.nio.fs.UnixPath
... ...
3729: 1 16 sun.util.resources.LocaleData$LocaleDataResourceBundleControl
Total 1077805 61629024
上述命令输出了内存中实例的数量和大小。
生成堆快照信息:
jmap -dump:format=b,file=heap.hprof 17338
2.5 jhat命令
jhat命令可以分析Java程序的堆快照。
[aaa@qq.com ~]# jhat heap.hprof
Reading from heap.hprof...
Dump file created Wed Mar 15 15:42:02 CST 2017
Snapshot read, resolving...
Resolving 1183100 objects...
Chasing references, expect 236 dots............................................................................................................................................................................................................................................
Eliminating duplicate references............................................................................................................................................................................................................................................
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.
可以通过浏览器访问7000端口查看堆快照信息。
2.6 jstack命令
用于输出Java应用程序的线程栈信息。
public class DeadLockMain {
private static String A = "A";
private static String B = "B";
public void deadLock() {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (A) {
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (B) {
System.out.println("thread1");
}
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (B) {
synchronized (A) {
System.out.println("thead2");
}
}
}
});
t1.start();
t2.start();
}
public static void main(String[] args) {
new DeadLockMain().deadLock();
}
}
上述代码是一个简单的会发生死锁的例子,两个线程分别持有A和B,并分别请求B和A,导致死锁产生。
使用jstack
打印上述进程的线程栈信息,部分输出如下:
"Thread-1" #9 prio=5 os_prio=0 tid=0x00007f0ff410b000 nid=0x56d9 waiting for monitor entry [0x00007f0fe4bfa000]
java.lang.Thread.State: BLOCKED (on object monitor)
at command.DeadLockMain$2.run(DeadLockMain.java:33)
- waiting to lock <0x00000000e2a77cc0> (a java.lang.String)
- locked <0x00000000e2a77cf0> (a java.lang.String)
at java.lang.Thread.run(Thread.java:745)
"Thread-0" #8 prio=5 os_prio=0 tid=0x00007f0ff40fb000 nid=0x56d8 waiting for monitor entry [0x00007f0fe4cfb000]
java.lang.Thread.State: BLOCKED (on object monitor)
at command.DeadLockMain$1.run(DeadLockMain.java:23)
- waiting to lock <0x00000000e2a77cf0> (a java.lang.String)
- locked <0x00000000e2a77cc0> (a java.lang.String)
at java.lang.Thread.run(Thread.java:745)
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x00007f0fd8004e28 (object 0x00000000e2a77cc0, a java.lang.String),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x00007f0fd80062c8 (object 0x00000000e2a77cf0, a java.lang.String),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at command.DeadLockMain$2.run(DeadLockMain.java:33)
- waiting to lock <0x00000000e2a77cc0> (a java.lang.String)
- locked <0x00000000e2a77cf0> (a java.lang.String)
at java.lang.Thread.run(Thread.java:745)
"Thread-0":
at command.DeadLockMain$1.run(DeadLockMain.java:23)
- waiting to lock <0x00000000e2a77cf0> (a java.lang.String)
- locked <0x00000000e2a77cc0> (a java.lang.String)
at java.lang.Thread.run(Thread.java:745)
Found 1 deadlock.
从上述输出来看,很容易找到死锁,并且可以看到发生死锁的两个线程以及两个线程的持有和等待的对象。
推荐阅读