线程转储是一个JVM活动线程的列表,它对于分析系统瓶颈和死锁非常有用。

首先介绍一下jdk自带的三个小工具

jdk自带工具

jps

显示指定系统内所有的HotSpot虚拟机线程

jmap

生成虚拟机的内存转储快照

jstack

显示虚拟机的线程快照

 现在实验一个简单死锁程序,程序示例可以直接查看Java很简单的死锁例子。那么如何生成内存转储快照或者如何显示线程快照呢?步骤如下

  1. 进入到当前程序运行的jdk的bin目录下,也就是这三个工具的所在目录。
  2. 命令行输入jps,查出当前虚拟机的所有线程的pid。
  3. 输入jmap -dump:format=b,<filename> <pid> 生成dump快照,其中format=b表示以二进制文件生成。<filename>表示文件名称 <pid>表示要生成那个线程的快照文件。这一步不是必须的。
  4. 输入jstack <pid>,就可以得到线程快照信息。

下面就从上面所提到的死锁案例进行分析:

第一步,第二步其中APPMain就是运行程序的主线程

C:\Users\Administrator>cd C:\Program Files\Java\jdk1.8.0_181\bin

C:\Program Files\Java\jdk1.8.0_181\bin>jps
7696
8288 Launcher
9572 Jps
2588 AppMain

第三步,在G文件夹下生成了a.dump的快照文件,如果需要对生成的文件进行查看,可以使用jdk自带工具VisualVM,或者更专业的dump文件分析工具Eclipse Memory Analuzer等。

C:\Program Files\Java\jdk1.8.0_181\bin>jmap -dump:format=b,file=G:/a.dump 2588
Dumping heap to G:\a.dump ...
Heap dump file created

第四步,生成进程快照信息,中间省略了一部分信息

C:\Program Files\Java\jdk1.8.0_181\bin>jstack 2588
2019-05-07 22:50:05
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.181-b13 mixed mode):

省略...

Found one Java-level deadlock:
=============================
"线程2":
  waiting to lock monitor 0x0000000056cabd08 (object 0x00000000dbb91cb8, a java.
lang.Object),
  which is held by "线程1"
"线程1":
  waiting to lock monitor 0x0000000056caa868 (object 0x00000000dbb91cc8, a java.
lang.Object),
  which is held by "线程2"

Java stack information for the threads listed above:
===================================================
"线程2":
        at test.LockTest$2.run(LockTest.java:41)
        - waiting to lock <0x00000000dbb91cb8> (a java.lang.Object)
        - locked <0x00000000dbb91cc8> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:748)
"线程1":
        at test.LockTest$1.run(LockTest.java:24)
        - waiting to lock <0x00000000dbb91cc8> (a java.lang.Object)
        - locked <0x00000000dbb91cb8> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

结果分析:

可以看出线程1和线程2死锁在对对象a锁的获取上。这里我不是很理解为什么线程1和线程2都是阻塞在a对象的获取上,两个线程waiting to lock,locked的地址是岔开的,也就是我理解的线程1持有对象a在等待b对象,线程2持有对象b在等待a对象,但是为什么括号里写的都是a对象,这个我不是很理解。

在Thread类中,有api也可以获得线程堆栈状态

Thread.currentThread().getStackTrace()可以获得当前线程的堆栈状态。

 

 

参考文献

[1]深入理解Java虚拟机