在之前的文章中,我们介绍了jdk14中jstat工具的使用,本文我们再深入探讨一下jstack工具的使用。
jstack工具主要用来打印java堆栈信息,主要是java的class名字,方法名,字节码索引,行数等信息。
jstack的命令格式
usage: jstack [-l][-e](to connect to running process) options: -l long listing. prints additional information about locks -e extended listing. prints additional information about threads -? -h --help -help to print this help message
jstack的参数比较简单,l可以包含锁的信息,e包含了额外的信息。
jstack的使用
我们举个例子:
jstack -l -e 53528
输出结果如下:
2020-05-09 21:46:51
full thread dump java hotspot(tm) 64-bit server vm (14.0.1 7 mixed mode, sharing):
threads class smr info:
_java_thread_list=0x00007fda0660eb00, length=14, elements={
0x00007fda04811000, 0x00007fda05845800, 0x00007fda05012000, 0x00007fda05847800,
0x00007fda05843800, 0x00007fda05854800, 0x00007fda0481f000, 0x00007fda0481f800,
0x00007fda04018800, 0x00007fda041ff800, 0x00007fda05a28800, 0x00007fda05b1a800,
0x00007fda05b1d800, 0x00007fda042be000
}
"reference handler" #2 daemon prio=10 os_prio=31 cpu=0.67ms elapsed=66335.21s allocated=0b defined_classes=0 tid=0x00007fda04811000 nid=0x4603 waiting on condition [0x000070000afe1000]
java.lang.thread.state: runnable
at java.lang.ref.reference.waitforreferencependinglist(java.base@14.0.1/native method)
at java.lang.ref.reference.processpendingreferences(java.base@14.0.1/reference.java:241)
at java.lang.ref.reference$referencehandler.run(java.base@14.0.1/reference.java:213)
locked ownable synchronizers:
- none
...
"vm thread" os_prio=31 cpu=1433.78ms elapsed=66335.22s tid=0x00007fda0506b000 nid=0x4803 runnable
"gc thread#0" os_prio=31 cpu=18.63ms elapsed=66335.23s tid=0x00007fda0502a800 nid=0x3203 runnable
"gc thread#1" os_prio=31 cpu=19.64ms elapsed=66334.06s tid=0x00007fda050e5800 nid=0x9d03 runnable
"gc thread#2" os_prio=31 cpu=17.72ms elapsed=66334.06s tid=0x00007fda05015000 nid=0x6203 runnable
"gc thread#3" os_prio=31 cpu=14.57ms elapsed=66332.78s tid=0x00007fda05138800 nid=0x6503 runnable
"g1 main marker" os_prio=31 cpu=0.25ms elapsed=66335.23s tid=0x00007fda05031000 nid=0x3303 runnable
"g1 conc#0" os_prio=31 cpu=14.85ms elapsed=66335.23s tid=0x00007fda05031800 nid=0x4b03 runnable
"g1 refine#0" os_prio=31 cpu=3.25ms elapsed=66335.23s tid=0x00007fda0583a800 nid=0x4a03 runnable
"g1 young remset sampling" os_prio=31 cpu=5929.79ms elapsed=66335.23s tid=0x00007fda0505a800 nid=0x3503 runnable
"vm periodic task thread" os_prio=31 cpu=21862.12ms elapsed=66335.13s tid=0x00007fda0505b000 nid=0xa103 waiting on condition
jni global refs: 43, weak refs: 45
输出的结果我们可以分为下面几个部分:
jvm虚拟机信息
第一部分是jvm虚拟机的信息
2020-05-09 21:46:51
full thread dump java hotspot(tm) 64-bit server vm (14.0.1 7 mixed mode, sharing):
上面显示了虚拟机的thread dump时间和虚拟机的版本等信息。
threads class smr info
第二部分是jvm中非jvm(非vm和非gc的线程)的内部线程信息。
threads class smr info:
_java_thread_list=0x00007fda0660eb00, length=14, elements={
0x00007fda04811000, 0x00007fda05845800, 0x00007fda05012000, 0x00007fda05847800,
0x00007fda05843800, 0x00007fda05854800, 0x00007fda0481f000, 0x00007fda0481f800,
0x00007fda04018800, 0x00007fda041ff800, 0x00007fda05a28800, 0x00007fda05b1a800,
0x00007fda05b1d800, 0x00007fda042be000
}
这些elements是和后面线程的tid相匹配的。表示的是本地线程对象的地址,注意这些不是线程的id。
大家可能注意到了里面写的是smr, smr全称是safe memory reclamation。
什么是smr呢?简单点讲就是安全的内存分配,一般这个问题会出现在非自动gc的编程语言中如c 。在这些语言中,需要自己来为对象分配内存和销毁对象,这样就可能导致在多线程的环境中,一个地址可能被分配给了多个对象,从而出现了内存分配的不安全。
线程信息
第三部分就是线程的具体信息了:
"reference handler" #2 daemon prio=10 os_prio=31 cpu=0.67ms elapsed=66335.21s allocated=0b defined_classes=0 tid=0x00007fda04811000 nid=0x4603 waiting on condition [0x000070000afe1000] java.lang.thread.state: runnable at java.lang.ref.reference.waitforreferencependinglist(java.base@14.0.1/native method) at java.lang.ref.reference.processpendingreferences(java.base@14.0.1/reference.java:241) at java.lang.ref.reference$referencehandler.run(java.base@14.0.1/reference.java:213) locked ownable synchronizers: - none
按照字段的顺序,我们可以把线程信息分为下面几个部分:
- 线程名字:例如reference handler
- 线程的id:例如#2
- 是否守护线程:例如daemon,daemon threads是低优先级的thread,它的作用是为user thread提供服务。 因为daemon threads的低优先级,并且仅为user thread提供服务,所以当所有的user thread都结束之后,jvm会自动退出,不管是否还有daemon threads在运行中。
- 优先级:例如prio=10
- os线程的优先级:例如os_prio=31
- cpu时间:线程获得cpu的时间,例如cpu=0.67ms
- elapsed:线程启动后经过的wall clock time
- allocated:本线程分配的分配的bytes数
- defined_classes:本线程定义的class个数
注意'allocated=' 和 ‘defined_classes=' 必须要开启 -xx: printextendedthreadinfo才会输出数据。
- address:java线程的地址,例如:tid=0x00007fda04811000
- os线程id:例如nid=0x4603
- 线程状态:例如waiting on condition
- 最新的java堆栈指针:最新的java堆栈指针sp,例如:[0x000070000afe1000]
接下来就是线程的堆栈信息:
java.lang.thread.state: runnable at java.lang.ref.reference.waitforreferencependinglist(java.base@14.0.1/native method) at java.lang.ref.reference.processpendingreferences(java.base@14.0.1/reference.java:241) at java.lang.ref.reference$referencehandler.run(java.base@14.0.1/reference.java:213)
上面的例子是线程的堆栈信息,并且列出来了线程的状态。
locked ownable synchronizer
接下来的部分是该线程拥有的,可用的用于同步的排它锁对象。
ownable synchronizer是一个同步器,这个同步器的同步属性是通过使用abstractownablesynchronizer或者它的子类来实现的。
例如reentrantlock和reentrantreadwritelock中的write-lock(注意不是read-lock,因为需要排它性)就是两个例子。
jvm threads
接下来是jvm的线程信息,因为这个线程是jvm内部的,所以没有线程id:
"vm thread" os_prio=31 cpu=1433.78ms elapsed=66335.22s tid=0x00007fda0506b000 nid=0x4803 runnable
"gc thread#0" os_prio=31 cpu=18.63ms elapsed=66335.23s tid=0x00007fda0502a800 nid=0x3203 runnable
"gc thread#1" os_prio=31 cpu=19.64ms elapsed=66334.06s tid=0x00007fda050e5800 nid=0x9d03 runnable
"gc thread#2" os_prio=31 cpu=17.72ms elapsed=66334.06s tid=0x00007fda05015000 nid=0x6203 runnable
"gc thread#3" os_prio=31 cpu=14.57ms elapsed=66332.78s tid=0x00007fda05138800 nid=0x6503 runnable
"g1 main marker" os_prio=31 cpu=0.25ms elapsed=66335.23s tid=0x00007fda05031000 nid=0x3303 runnable
"g1 conc#0" os_prio=31 cpu=14.85ms elapsed=66335.23s tid=0x00007fda05031800 nid=0x4b03 runnable
"g1 refine#0" os_prio=31 cpu=3.25ms elapsed=66335.23s tid=0x00007fda0583a800 nid=0x4a03 runnable
"g1 young remset sampling" os_prio=31 cpu=5929.79ms elapsed=66335.23s tid=0x00007fda0505a800 nid=0x3503 runnable
"vm periodic task thread" os_prio=31 cpu=21862.12ms elapsed=66335.13s tid=0x00007fda0505b000 nid=0xa103 waiting on condition
jni references
最后一部分是jni(java native interface)引用的信息,注意这些引用可能会导致内存泄露,因为这些native的引用并不会被自动垃圾回收。
jni global refs: 43, weak refs: 45
jstack是分析线程的非常强大的工具,希望大家能够使用起来。