概述
性能诊断是软件工程师在日常工作中需要经常面对和解决的问题,在用户体验至上的今天,解决好应用的性能问题能带来非常大的收益。
Java 作为最流行的编程语言之一,其应用性能诊断一直受到业界广泛关注。可能造成 Java 应用出现性能问题的因素非常多,例如线程控制、磁盘读写、数据库访问、网络 I/O、垃圾收集等。想要定位这些问题,一款优秀的性能诊断工具必不可少。
体会 1:使用数据说明问题,使用知识分析问题,使用工具处理问题。
体会 2:无监控、不调优。
简单命令行工具
在我们刚接触 java 学习的时候,大家肯定最先了解的两个命令就是 javac
,java
,那么除此之外,还有没有其他的命令可以供我们使用呢?
我们进入到安装 jdk 的 bin 目录,发现还有一系列辅助工具。这些辅助工具用来获取目标 JVM 不同方面、不同层次的信息,帮助开发人员很好地解决 Java 应用程序的一些疑难杂症。
官方源码地址:
http://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/jdk.jcmd/share/classes/sun/tools
jps:查看正在运行的 Java 进程
jps(Java Process Status):显示指定系统内所有的 HotSpot 虚拟机进程(查看虚拟机进程信息),可用于查询正在运行的虚拟机进程。
说明:对于本地虚拟机进程来说,进程的本地虚拟机 ID 与操作系统的进程 ID 是一致的,是唯一的。
基本使用语法为:jps [options] [hostid]
。
代码示例
public class ScannerTest {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String info = scanner.next();
}
}
运行后,在命令行输入 jps 查看进程:
我们还可以通过追加参数,来打印额外的信息。
options 参数
- -q:仅仅显示 LVMID(local virtual machine id),即本地虚拟机唯一 id。不显示主类的名称等
- -l:输出应用程序主类的全类名或如果进程执行的是 jar 包,则输出 jar 完整路径
- -m:输出虚拟机进程启动时传递给主类 main() 的参数
- -v:列出虚拟机进程启动时的 JVM 参数。比如:
-Xms20m -Xmx50m
是启动程序指定的 jvm 参数
说明:以上参数可以综合使用。
如图:
补充:如果某 Java 进程关闭了默认开启的 UsePerfData 参数(即使用参数 -XX:-UsePerfData
),那么 jps 命令(以及下面介绍的 jstat)将无法探知该 Java 进程。
hostid 参数
RMI 注册表中注册的主机名。如果想要远程监控主机上的 java 程序,需要安装 jstatd。
对于具有更严格的安全实践的网络场所而言,可能使用一个自定义的策略文件来显示对特定的可信主机或网络的访问,尽管这种技术容易受到 IP 地址欺诈攻击。
如果安全问题无法使用一个定制的策略文件来处理,那么最安全的操作是不运行 jstatd 服务器,而是在本地使用 jstat 和 jps 工具。
jstat:查看 JVM 统计信息
官方文档:
https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstat.html
jstat(JVM Statistics Monitoring Tool):用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程虚拟机进程中的类装载、内存、垃圾收集、JIT 编译等运行数据。在没有 GUI 图形界面,只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的首选工具。常用于检测垃圾回收问题以及内存泄漏问题。
基本使用语法为:jstat - [-t] [-h] [ []]
。
查看命令相关参数:jstat-h
或 jstat-help
。
其中 vmid 是进程 id 号,也就是 jps 之后看到的前面的号码,如下:
option参数
选项 option 可以由以下值构成:
类装载相关的:
- -class:显示 ClassLoader 的相关信息:类的装载、卸载数量、总空间、类装载所消耗的时间等
垃圾回收相关的:
- -gc:显示与 GC 相关的堆信息。包括 Eden 区、两个 Survivor 区、老年代、永久代等的容量、已用空间、GC 时间合计等信息
- -gccapacity:显示内容与
-gc
基本相同,但输出主要关注 Java 堆各个区域使用到的最大、最小空间 - -gcutil:显示内容与
-gc
基本相同,但输出主要关注已使用空间占总空间的百分比 - -gccause:与
-gcutil
功能一样,但是会额外输出导致最后一次或当前正在发生的 GC 产生的原因 - -gcnew:显示新生代 GC 状况
- -gcnewcapacity:显示内容与
-gcnew
基本相同,输出主要关注使用到的最大、最小空间 - -geold:显示老年代 GC 状况
- -gcoldcapacity:显示内容与
-gcold
基本相同,输出主要关注使用到的最大、最小空间 - -gcpermcapacity:显示永久代使用到的最大、最小空间
JIT 相关的:
- -compiler:显示 JIT 编译器编译过的方法、耗时等信息
- -printcompilation:输出已经被 JIT 编译的方法
jstat -class
jstat -compiler
显示 JIT 编译器编译过的方法、耗时等信息。
jstat -printcompilation
输出已经被 JIT 编译的方法。
jstat -gc
显示与 GC 相关的堆信息。包括 Eden 区、两个 Survivor 区、老年代、永久代等的容量、已用空间、GC 时间合计等信息。
执行代码:
public class GCTest {
public static void main(String[] args) {
ArrayList list = new ArrayList();
for (int i = 0; i < 1000; i++) {
byte[] arr = new byte[1024 * 100];//100KB
list.add(arr);
try {
Thread.sleep(120);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
JVM 参数:
-Xms60m -Xmx60m -XX:SurvivorRatio=8
运行后利用命令查询:
表头 | 含义(字节) |
---|---|
S0C | 幸存者 0 区的大小 |
S1C | 幸存者 1 区的大小 |
S0U | 幸存者 0 区已使用的大小 |
S1U | 幸存者 1 区已使用的大小 |
EC | Eden 区的大小 |
EU | Eden 区已使用的大小 |
OC | 老年代的大小 |
OU | 老年代已使用的大小 |
MC | 元空间的大小 |
MU | 元空间已使用的大小 |
CCSC | 压缩类空间的大小 |
CCSU | 压缩类空间已使用的大小 |
YGC | 从应用程序启动到采样时 Young GC 的次数 |
YGCT | 从应用程序启动到采样时 Young GC 消耗时间(秒) |
FGC | 从应用程序启动到采样时 Full GC 的次数 |
FGCT | 从应用程序启动到采样时的 Full GC 的消耗时间(秒) |
GCT | 从应用程序启动到采样时 GC 的总时间 |
后面的参数代表 1000 毫秒打印一次,一个打印 10 次。
jstat -gccapacity
显示内容与 -gc
基本相同,但输出主要关注 Java 堆各个区域使用到的最大、最小空间。
jstat -gcutil
显示内容与 -gc
基本相同,但输出主要关注已使用空间占总空间的百分比。
表头 | 含义(字节) |
---|---|
SO | Survivor 0 区空间百分比 |
S1 | Survivor 1 区空间百分比 |
E | Eden 区空间百分比 |
O | Old 区空间百分比 |
N | 方法区空间百分比 |
CCS | 压缩空间百分比 |
YGC | 从应用程序启动到采样时 Young GC 的次数 |
YGCT | 从应用程序启动到采样时 Young GC 消耗时间(秒) |
FGC | 从应用程序启动到采样时 Full GC 的次数 |
FGCT | 从应用程序启动到采样时的 Full GC 的消耗时间(秒) |
GCT | 从应用程序启动到采样时 GC 的总时间 |
jstat -gccause
与 -gcutil
功能一样,但是会额外输出导致最后一次或当前正在发生的 GC 产生的原因。
jstat -gcnew
显示新生代 GC 状况。
jstat -gcnewcapacity
显示内容与 -gcnew
基本相同,输出主要关注使用到的最大、最小空间。
jstat -gcold
显示老年代 GC 状况。
jstat -gcoldcapacity
显示内容与 -gcold
基本相同,输出主要关注使用到的最大、最小空间。
其他参数
下面的参数都是配合 option 参数后面使用。
基本使用语法为:jstat - [-t] [-h] [ []]
。
-
interval 参数:用于指定输出统计数据的周期,单位为毫秒。即:查询间隔
-
count 参数:用于指定查询的总次数
-
-t 参数:可以在输出信息前加上一个 Timestamp 列,显示程序的运行时间。单位:秒
我们可以比较 Java 进程的启动时间以及总 GC 时间(GCT 列),或者两次测量的间隔时间以及总 GC 时间的增量,来得出 GC 时间占运行时间的比例。
如果该比例超过 20%,则说明目前堆的压力较大;如果该比例超过 90%,则说明堆里几乎没有可用空间,随时都可能抛出 OOM 异常。
-
-h 参数:可以在周期性数据输出时,输出多少行数据后输出一个表头信息
jstat -t
可以在输出信息前加上一个 Timestamp 列,显示程序的运行时间。单位:秒。
jstat -t -h
可以在周期性数据输出时,输出多少行数据后输出一个表头信息。
其他功能
jstat 还可以用来判断是否出现内存泄漏:
- 第 1 步:在长时间运行的 Java 程序中,我们可以运行 jstat 命令连续获取多行性能数据,并取这几行数据中 OU 列(即已占用的老年代内存)的最小值
- 第 2 步:然后,我们每隔一段较长的时间重复一次上述操作,来获得多组 OU 最小值。如果这些值呈上涨趋势,则说明该 Java 程序的老年代内存已使用量在不断上涨,这意味着无法回收的对象在不断增加,因此很有可能存在内存泄漏
jinfo:实时查看和修改 JVM 配置参数
jinfo(Configuration Info for Java):查看虚拟机配置参数信息,也可用于调整虚拟机的配置参数。在很多情况卡,Java 应用程序不会指定所有的 Java 虚拟机参数。而此时,开发人员可能不知道某一个具体的 Java 虚拟机参数的默认值。在这种情况下,可能需要通过查找文档获取某个参数的默认值。这个查找过程可能是非常艰难的。但有了 jinfo 工具,开发人员可以很方便地找到 Java 虚拟机参数的当前值。
jinfo 不仅可以查看运行时某一个 Java 虚拟机参数的实际取值,甚至可以在运行时修改部分参数,并使之立即生效。
但是,并非所有参数都支持动态修改。参数只有被标记为 manageable 的 flag 可以被实时修改。其实,这个修改能力是极其有限的。
基本使用语法为:jinfo [options] pid
。
说明:java 进程 ID 必须要加上。
选项 | 选项说明 |
---|---|
no option | 输出全部的参数和系统属性 |
-flag name | 输出对应名称的参数 |
-flag [+-]name | 开启或者关闭对应名称的参数 只有被标记为 manageable 的参数才可以被动态修改 |
-flag name=value | 设定对应名称的参数 |
-flags | 输出全部的参数 |
-sysprops | 输出系统属性 |
参数查看
jinfo -sysprops
> jinfo -sysprops
jboss.modules.system.pkgs = com.intellij.rt
java.vendor = Oracle Corporation
sun.java.launcher = SUN_STANDARD
sun.management.compiler = HotSpot 64-Bit Tiered Compilers
catalina.useNaming = true
os.name = Windows 10
...
jinfo -flags
C:\Users\24560>jps
13968 GCTest
22016 Launcher
8912
17740 Jps
C:\Users\24560>jinfo -flags 13968
# 输出结果如下:
Attaching to process ID 13968, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.231-b11
Non-default VM flags: -XX:CICompilerCount=4 -XX:InitialHeapSize=62914560 -XX:MaxHeapSize=62914560 -XX:MaxNewSize=20971520 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=20971520 -XX:OldSize=41943040 -XX:SurvivorRatio=8 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
Command line: -Xms60m -Xmx60m -XX:SurvivorRatio=8 -javaagent:F:\Programming area\Ideal\IntelliJ IDEA 2020.3\lib\idea_rt.jar=55919:F:\Programming area\Ideal\IntelliJ IDEA 2020.3\bin -Dfile.encoding=UTF-8
jinfo -flag name
查看当前的 Java 程序是否使用了指定的参数。
> jinfo -flag UseParallelGC 13968
-XX:+UseParallelGC
> jinfo -flag UseG1GC 13968
-XX:-UseG1GC
参数修改
jinfo -flag [+-]name
开启或者关闭对应名称的参数 只有被标记为 manageable 的参数才可以被动态修改。
> jinfo -flag +PrintGCDetails 13968
> jinfo -flag PrintGCDetails 13968
-XX:+PrintGCDetails
> jinfo -flag -PrintGCDetails 13968
> jinfo -flag PrintGCDetails 13968
-XX:-PrintGCDetails
参数拓展
java -XX:+PrintFlagsInitial
查看所有 JVM 参数启动的初始值。
[Global flags]
intx ActiveProcessorCount = -1 {product}
uintx AdaptiveSizeDecrementScaleFactor = 4 {product}
uintx AdaptiveSizeMajorGCDecayTimeScale = 10 {product}
uintx AdaptiveSizePausePolicy = 0 {product}
...
java -XX:+PrintFlagsFinal
查看所有 JVM 参数的最终值。
java -XX:+PrintFlagsFinal -version | grep manageable
:可以查看被标记为 manageable 的参数。
manageable 参数就是在 Java 程序启动后还能重新设置的参数,如 GC 就不能在启动程序后再重新修改,所以不是 manageable 参数。
[Global flags]
intx ActiveProcessorCount = -1 {product}
...
intx CICompilerCount := 4 {product}
uintx InitialHeapSize := 333447168 {product}
uintx MaxHeapSize := 1029701632 {product}
uintx MaxNewSize := 1774714880 {product}
java -XX:+PrintCommandLineFlags
查看哪些已经被用户或者 JVM 设置过的详细的 XX 参数的名称和值。
-XX:InitialHeapSize=332790016 -XX:MaxHeapSize=5324640256 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
jmap:导出内存映像文件&内存使用情况
官方帮助文档:
https://docs.oracle.com/en/java/javase/11/tools/jmap.html
jmap(JVM Memory Map):作用一方面是获取 dump 文件(堆转储快照文件,二进制文件),它还可以获取目标 Java 进程的内存相关信息,包括 Java 堆各区域的使用情况、堆中对象的统计信息、类加载信息等。开发人员可以在控制台中输入命令 jmap -help
查阅 jmap 工具的具体使用方式和一些标准选项配置。
基本语法
基本使用语法为:
jmap [option]
jmap [option]
jmap -dump:live,format=b,file=
使用如图:
在 D 盘生成了 4 个文件,其中 4.hprof 用了第二个命令生成。
文件的大小依次递增,因为运行越久,里面的对象越多,占用的空间越大。
建议使用
jmap -dump:live,format=b,file=
命令生成 dump 文件,因为只需要获取存活的对象,没必要把被回收或者死亡的对象 dump 到文件里,导致文件太大。自动方式
当程序发生 OOM 退出系统时,一些瞬时信息都随着程序的终止而消失,而重现 OOM 问题往往比较困难或者耗时。此时若能在 OOM 时,自动导出 dump 文件就显得非常迫切。
这里介绍一种比较常用的取得堆快照文件的方法,即使用:
-XX:HeapDumpOnOutOfMemoryError
:在程序发生 OOM 时,导出应用程序的当前堆快照。-XX:HeapDumpPath=
:可以指定堆快照的保存位置。代码示例如下:
public class GCTest { public static void main(String[] args) { ArrayList list = new ArrayList(); for (int i = 0; i < 1000; i++) { byte[] arr = new byte[1024 * 100];//100KB list.add(arr); try { Thread.sleep(60); } catch (InterruptedException e) { e.printStackTrace(); } } } }
设置 JVM 参数:
-Xms60m -Xmx60m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\5.hprof
运行代码后,当发生 OOM 异常,则立马在 D 盘生成 5.hprof 文件。
显示堆内存相关信息
两个常用命令:
jmap -heap pid
jmap -histo pid
命令行示例:
jps 17744 Launcher 1904 GCTest 8912 15316 Jps jmap -heap 3540 > a.txt jmap -histo 1904 > b.txt
两个命令将信息分别写到了 a.txt、b.txt 文件。
a.txt 内容为:
Attaching to process ID 1904, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.231-b11 using thread-local object allocation. Parallel GC with 10 thread(s) Heap Configuration: MinHeapFreeRatio = 0 MaxHeapFreeRatio = 100 MaxHeapSize = 4255121408 (4058.0MB) NewSize = 88604672 (84.5MB) MaxNewSize = 1418199040 (1352.5MB) OldSize = 177733632 (169.5MB) NewRatio = 2 SurvivorRatio = 8 MetaspaceSize = 21807104 (20.796875MB) CompressedClassSpaceSize = 1073741824 (1024.0MB) MaxMetaspaceSize = 17592186044415 MB G1HeapRegionSize = 0 (0.0MB) Heap Usage: PS Young Generation Eden Space: capacity = 66584576 (63.5MB) used = 31136256 (29.69384765625MB) free = 35448320 (33.80615234375MB) 46.761964812992126% used From Space: capacity = 11010048 (10.5MB) used = 0 (0.0MB) free = 11010048 (10.5MB) 0.0% used To Space: capacity = 11010048 (10.5MB) used = 0 (0.0MB) free = 11010048 (10.5MB) 0.0% used PS Old Generation capacity = 177733632 (169.5MB) used = 0 (0.0MB) free = 177733632 (169.5MB) 0.0% used 3163 interned Strings occupying 259640 bytes.
b.txt 内容:
num #instances #bytes class name ---------------------------------------------- 1: 1610 44293784 [B 2: 659 2906760 [I 3: 7774 906192 [C 4: 5933 142392 java.lang.String 5: 691 79112 java.lang.Class 6: 1305 67680 [Ljava.lang.Object; 7: 791 31640 java.util.TreeMap$Entry 8: 628 25120 java.util.LinkedHashMap$Entry 9: 456 22168 [Ljava.lang.String; 10: 364 11648 java.util.HashMap$Node 11: 37 11504 [Ljava.util.HashMap$Node; 12: 152 10944 java.lang.reflect.Field 13: 438 10512 java.lang.StringBuilder 14: 242 7744 java.util.Hashtable$Entry 15: 233 7456 java.io.File 16: 101 6464 java.net.URL 17: 241 5784 java.lang.StringBuffer 18: 125 5000 java.lang.ref.SoftReference 19: 258 4128 java.lang.Integer 20: 117 3744 java.util.concurrent.ConcurrentHashMap$Node 21: 25 3648 [Ljava.util.Hashtable$Entry; 22: 75 3600 java.nio.HeapCharBuffer 23: 74 3552 java.nio.HeapByteBuffer 24: 42 3360 [S 25: 8 3008 java.lang.Thread 26: 5 2568 [J 27: 20 2496 [Ljava.util.concurrent.ConcurrentHashMap$Node; 28: 44 2464 sun.misc.URLClassPath$JarLoader 29: 2 2384 [[Ljava.lang.Object; 30: 89 2136 java.net.Parts 31: 2 2080 [[C 32: 26 2080 java.lang.reflect.Constructor 33: 39 1872 sun.util.locale.LocaleObjectCache$CacheEntry 34: 43 1720 java.lang.ref.Finalizer 35: 34 1632 java.util.HashMap 36: 1 1568 [[B 37: 37 1480 java.io.ObjectStreamField 38: 26 1456 java.lang.Class$ReflectionData 39: 91 1456 java.lang.Object 40: 20 1280 java.util.concurrent.ConcurrentHashMap 41: 15 1200 [Ljava.util.WeakHashMap$Entry; 42: 45 1080 sun.misc.URLClassPath$3 43: 2 1064 [Ljava.lang.invoke.MethodHandle; 44: 11 1056 java.util.jar.JarFile$JarFileEntry 45: 1 1040 [Ljava.lang.Integer; 46: 41 984 java.io.ExpiringCache$Entry 47: 11 880 java.util.zip.ZipEntry 48: 22 880 sun.util.locale.BaseLocale$Key 49: 47 840 [Ljava.lang.Class; 50: 15 840 sun.nio.cs.UTF_8$Encoder 51: 34 816 sun.security.action.GetPropertyAction 52: 12 768 java.util.jar.JarFile 53: 12 752 [Ljava.lang.reflect.Field; 54: 15 720 java.util.WeakHashMap 55: 22 704 java.lang.ref.ReferenceQueue 56: 17 680 java.util.WeakHashMap$Entry 57: 12 672 java.util.zip.ZipFile$ZipFileInputStream 58: 19 608 java.util.Locale 59: 19 608 sun.util.locale.BaseLocale 60: 14 560 sun.nio.cs.UTF_8$Decoder 61: 22 528 java.util.Locale$LocaleKey 62: 13 520 java.security.AccessControlContext 63: 21 504 java.util.jar.Attributes$Name 64: 18 432 sun.misc.MetaIndex 65: 10 400 java.io.FileDescriptor 66: 13 392 [Ljava.io.ObjectStreamField; 67: 1 384 com.intellij.rt.execution.application.AppMainV2$1 68: 1 384 java.lang.ref.Finalizer$FinalizerThread 69: 24 384 java.lang.ref.ReferenceQueue$Lock 70: 6 384 java.nio.DirectByteBuffer 71: 16 384 java.util.ArrayList 72: 12 384 java.util.zip.ZipCoder 73: 1 376 java.lang.ref.Reference$ReferenceHandler 74: 4 352 java.lang.reflect.Method 75: 6 336 java.nio.DirectLongBufferU 76: 7 336 java.util.Hashtable 77: 7 336 java.util.Hashtable$Enumerator 78: 6 336 java.util.LinkedHashMap 79: 14 336 sun.misc.FileURLMapper 80: 6 336 sun.nio.cs.ext.DoubleByte$Decoder 81: 10 320 java.lang.OutOfMemoryError 82: 10 320 java.util.Vector 83: 3 312 [D 84: 13 312 [Ljava.lang.reflect.Constructor; 85: 13 312 java.lang.Class$1 86: 13 312 sun.reflect.NativeConstructorAccessorImpl 87: 12 288 java.util.ArrayDeque 88: 6 288 java.util.Properties 89: 3 280 [[Ljava.lang.String; 90: 5 280 java.util.ResourceBundle$CacheKey 91: 5 280 sun.util.calendar.ZoneInfo 92: 3 240 [Ljava.lang.ThreadLocal$ThreadLocalMap$Entry; 93: 3 232 [Ljava.net.URL; 94: 7 224 java.lang.ThreadLocal$ThreadLocalMap$Entry 95: 7 224 java.security.CodeSource 96: 14 224 sun.misc.URLClassPath$JarLoader$1 97: 9 216 java.util.LinkedList$Node 98: 2 208 sun.net.www.protocol.file.FileURLConnection 99: 13 208 sun.reflect.DelegatingConstructorAccessorImpl 100: 5 200 java.security.ProtectionDomain 101: 3 192 [Ljava.io.File; 102: 4 192 java.io.BufferedReader 103: 6 192 java.io.FileInputStream 104: 8 192 java.net.URLClassLoader$1 105: 4 192 java.util.TreeMap 106: 4 192 sun.nio.cs.StreamDecoder 107: 4 192 sun.nio.cs.StreamEncoder 108: 3 192 sun.nio.cs.ext.DoubleByte$Encoder 109: 2 192 sun.util.calendar.Gregorian$Date 110: 4 160 java.io.BufferedWriter 111: 4 160 java.lang.ClassLoader$NativeLibrary 112: 4 160 java.lang.ClassNotFoundException 113: 4 160 java.security.PrivilegedActionException 114: 5 160 java.util.LinkedList 115: 5 160 java.util.ResourceBundle$LoaderReference 116: 5 160 sun.util.locale.provider.LocaleProviderAdapter$Type 117: 3 144 java.util.StringTokenizer 118: 6 144 sun.misc.PerfCounter 119: 3 144 sun.misc.URLClassPath 120: 6 144 sun.usagetracker.UsageTrackerClient$1 121: 2 128 java.io.ExpiringCache$1 122: 4 128 java.lang.StringCoding$StringDecoder 123: 4 128 java.lang.StringCoding$StringEncoder 124: 4 128 java.util.Stack 125: 4 128 java.util.concurrent.ConcurrentHashMap$ForwardingNode 126: 3 120 java.io.BufferedInputStream 127: 1 120 java.net.SocksSocketImpl 128: 5 120 java.util.Collections$UnmodifiableRandomAccessList 129: 5 120 sun.misc.FloatingDecimal$PreparedASCIIToBinaryBuffer 130: 7 112 java.lang.Class$3 131: 2 112 java.lang.Package 132: 2 112 java.util.zip.ZipFile$ZipFileInflaterInputStream 133: 2 112 sun.nio.cs.ISO_8859_1$Encoder 134: 3 96 java.io.FileOutputStream 135: 3 96 java.io.FilePermission 136: 4 96 java.io.OutputStreamWriter 137: 4 96 java.lang.RuntimePermission 138: 2 96 java.lang.ThreadGroup 139: 3 96 java.lang.ref.WeakReference 140: 3 96 java.util.ArrayList$Itr 141: 6 96 java.util.HashSet 142: 2 96 java.util.ResourceBundle$BundleReference 143: 2 96 java.util.zip.Inflater 144: 1 96 sun.misc.Launcher$AppClassLoader 145: 3 96 sun.misc.URLClassPath$JarLoader$2 146: 3 96 sun.net.spi.DefaultProxySelector$NonProxyInfo 147: 1 88 java.net.DualStackPlainSocketImpl 148: 1 88 sun.misc.Launcher$ExtClassLoader 149: 5 80 [Ljava.security.Principal; 150: 2 80 [Lsun.util.locale.provider.LocaleProviderAdapter$Type; 151: 2 80 java.io.ExpiringCache 152: 5 80 java.io.FileInputStream$1 153: 5 80 java.lang.ClassLoader$3 154: 5 80 java.lang.ThreadLocal 155: 1 80 java.net.URI 156: 5 80 java.security.ProtectionDomain$Key 157: 2 80 java.util.ServiceLoader$LazyIterator 158: 2 80 sun.misc.FloatingDecimal$BinaryToASCIIBuffer 159: 2 80 sun.misc.URLClassPath$1 160: 2 80 sun.misc.URLClassPath$2 161: 2 80 sun.util.locale.LanguageTag 162: 3 72 [Ljava.lang.reflect.Method; 163: 3 72 java.lang.ThreadLocal$ThreadLocalMap 164: 3 72 java.net.Proxy$Type 165: 3 72 java.util.Arrays$ArrayList 166: 3 72 java.util.Collections$SynchronizedSet 167: 1 72 java.util.ResourceBundle$RBClassLoader 168: 3 72 java.util.concurrent.atomic.AtomicLong 169: 3 72 sun.misc.FloatingDecimal$ExceptionalBinaryToASCIIBuffer 170: 1 72 sun.util.locale.provider.JRELocaleProviderAdapter 171: 1 64 [F 172: 2 64 [Ljava.lang.Thread; 173: 2 64 java.io.DataInputStream 174: 2 64 java.io.FileNotFoundException 175: 2 64 java.io.PrintStream 176: 2 64 java.lang.ClassValue$Entry 177: 2 64 java.lang.NoSuchMethodError 178: 2 64 java.lang.VirtualMachineError 179: 2 64 java.lang.ref.ReferenceQueue$Null 180: 2 64 java.security.BasicPermissionCollection 181: 2 64 java.security.Permissions 182: 2 64 java.util.LinkedHashMap$LinkedEntryIterator 183: 2 64 java.util.ServiceLoader 184: 4 64 sun.reflect.ReflectionFactory$1 185: 2 48 [Ljava.util.Enumeration; 186: 2 48 java.io.BufferedOutputStream 187: 2 48 java.io.ByteArrayOutputStream 188: 2 48 java.io.File$PathStatus 189: 3 48 java.io.FilePermission$1 190: 2 48 java.io.FilePermissionCollection 191: 2 48 java.io.FileReader 192: 2 48 java.io.InputStreamReader 193: 2 48 java.net.InetAddress$Cache 194: 2 48 java.net.InetAddress$Cache$Type 195: 1 48 java.net.SocketInputStream 196: 2 48 java.net.URLClassLoader$3 197: 2 48 java.nio.charset.CoderResult 198: 3 48 java.nio.charset.CodingErrorAction 199: 2 48 java.util.Date 200: 1 48 java.util.Properties$LineReader 201: 2 48 java.util.ServiceLoader$1 202: 2 48 java.util.zip.ZStreamRef 203: 2 48 sun.instrument.InstrumentationImpl$1 204: 2 48 sun.misc.CompoundEnumeration 205: 2 48 sun.misc.NativeSignalHandler 206: 2 48 sun.misc.Signal 207: 2 48 sun.net.www.MessageHeader 208: 3 48 sun.net.www.ParseUtil 209: 3 48 sun.net.www.protocol.jar.Handler 210: 2 48 sun.nio.cs.Surrogate$Parser 211: 1 48 sun.nio.cs.US_ASCII$Decoder 212: 3 48 sun.reflect.ReflectionFactory$GetReflectionFactoryAction 213: 1 48 sun.util.locale.provider.LocaleResources$ResourceReference 214: 1 48 sun.util.resources.TimeZoneNames 215: 1 48 sun.util.resources.en.TimeZoneNames_en 216: 1 40 java.util.ResourceBundle$1 217: 1 40 sun.nio.cs.StandardCharsets$Aliases 218: 1 40 sun.nio.cs.StandardCharsets$Cache 219: 1 40 sun.nio.cs.StandardCharsets$Classes 220: 1 40 sun.nio.cs.ext.ExtendedCharsets 221: 1 32 [Ljava.lang.OutOfMemoryError; 222: 2 32 [Ljava.lang.StackTraceElement; 223: 1 32 [Ljava.lang.ThreadGroup; 224: 1 32 [Ljava.net.Proxy$Type; 225: 1 32 java.io.ByteArrayInputStream 226: 1 32 java.io.WinNTFileSystem 227: 1 32 java.lang.ArithmeticException 228: 2 32 java.lang.Boolean 229: 2 32 java.lang.ClassLoader$2 230: 1 32 java.lang.InternalError 231: 1 32 java.lang.NullPointerException 232: 1 32 java.net.InetAddress$InetAddressHolder 233: 1 32 java.net.Socket 234: 1 32 java.net.URI$Parser 235: 2 32 java.net.URLClassLoader$3$1 236: 2 32 java.nio.ByteOrder 237: 2 32 java.util.Hashtable$KeySet 238: 2 32 java.util.LinkedHashMap$LinkedEntrySet 239: 2 32 java.util.LinkedHashMap$LinkedKeySet 240: 2 32 java.util.concurrent.atomic.AtomicInteger 241: 1 32 java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl 242: 1 32 java.util.jar.Manifest$FastInputStream 243: 1 32 sun.instrument.InstrumentationImpl 244: 1 32 sun.misc.FloatingDecimal$ASCIIToBinaryBuffer 245: 2 32 sun.misc.PostVMInitHook$1 246: 1 32 sun.misc.URLClassPath$FileLoader$1 247: 1 32 sun.net.spi.DefaultProxySelector$3 248: 1 32 sun.nio.cs.StandardCharsets 249: 2 32 sun.usagetracker.UsageTrackerClient$2 250: 2 32 sun.usagetracker.UsageTrackerClient$3 251: 1 32 sun.util.locale.provider.LocaleResources 252: 1 32 sun.util.locale.provider.LocaleServiceProviderPool 253: 1 24 [Ljava.io.File$PathStatus; 254: 1 24 [Ljava.lang.ClassValue$Entry; 255: 1 24 [Ljava.net.InetAddress$Cache$Type; 256: 1 24 [Ljava.net.InetAddress; 257: 1 24 [Ljava.security.ProtectionDomain; 258: 1 24 [Lsun.launcher.LauncherHelper; 259: 1 24 java.lang.Class$MethodArray 260: 1 24 java.lang.ClassValue$Version 261: 1 24 java.lang.invoke.MethodHandleImpl$4 262: 1 24 java.lang.reflect.ReflectPermission 263: 1 24 java.net.Inet4Address 264: 1 24 java.net.Inet6AddressImpl 265: 1 24 java.net.InetSocketAddress$InetSocketAddressHolder 266: 1 24 java.net.Proxy 267: 1 24 java.util.BitSet 268: 1 24 java.util.Collections$EmptyMap 269: 1 24 java.util.Collections$SetFromMap 270: 1 24 java.util.Collections$UnmodifiableCollection$1 271: 1 24 java.util.Locale$Cache 272: 1 24 java.util.ResourceBundle$Control$CandidateListCache 273: 1 24 java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl$1 274: 1 24 java.util.jar.Manifest 275: 1 24 sun.instrument.TransformerManager 276: 1 24 sun.launcher.LauncherHelper 277: 1 24 sun.misc.JarIndex 278: 1 24 sun.misc.Launcher$AppClassLoader$1 279: 1 24 sun.misc.URLClassPath$FileLoader 280: 1 24 sun.nio.cs.ISO_8859_1 281: 1 24 sun.nio.cs.ThreadLocalCoders$1 282: 1 24 sun.nio.cs.ThreadLocalCoders$2 283: 1 24 sun.nio.cs.US_ASCII 284: 1 24 sun.nio.cs.UTF_16 285: 1 24 sun.nio.cs.UTF_16BE 286: 1 24 sun.nio.cs.UTF_16LE 287: 1 24 sun.nio.cs.UTF_8 288: 1 24 sun.nio.cs.ext.GBK 289: 1 24 sun.reflect.NativeMethodAccessorImpl 290: 1 24 sun.usagetracker.UsageTrackerClient$4 291: 1 24 sun.util.locale.BaseLocale$Cache 292: 1 24 sun.util.locale.provider.SPILocaleProviderAdapter$1 293: 1 24 sun.util.locale.provider.TimeZoneNameProviderImpl 294: 1 24 sun.util.resources.LocaleData$1 295: 1 16 [Ljava.lang.Throwable; 296: 1 16 [Ljava.security.cert.Certificate; 297: 1 16 [Lsun.instrument.TransformerManager$TransformerInfo; 298: 1 16 [Lsun.util.calendar.ZoneInfoFile$ZoneOffsetTransitionRule; 299: 1 16 java.io.FileDescriptor$1 300: 1 16 java.io.FileOutputStream$1 301: 1 16 java.lang.CharacterDataLatin1 302: 1 16 java.lang.ClassValue$Identity 303: 1 16 java.lang.Compiler$1 304: 1 16 java.lang.Runtime 305: 1 16 java.lang.String$CaseInsensitiveComparator 306: 1 16 java.lang.System$2 307: 1 16 java.lang.SystemClassLoaderAction 308: 1 16 java.lang.Terminator$1 309: 1 16 java.lang.invoke.MemberName$Factory 310: 1 16 java.lang.invoke.MethodHandleImpl$1 311: 1 16 java.lang.invoke.MethodHandleImpl$2 312: 1 16 java.lang.invoke.MethodHandleImpl$3 313: 1 16 java.lang.invoke.MethodHandleStatics$1 314: 1 16 java.lang.ref.Reference$1 315: 1 16 java.lang.ref.Reference$Lock 316: 1 16 java.lang.reflect.ReflectAccess 317: 1 16 java.net.AbstractPlainSocketImpl$1 318: 1 16 java.net.InetAddress$1 319: 1 16 java.net.InetAddress$2 320: 1 16 java.net.InetSocketAddress 321: 1 16 java.net.PlainSocketImpl$1 322: 1 16 java.net.Socket$2 323: 1 16 java.net.SocksSocketImpl$3 324: 1 16 java.net.URLClassLoader$7 325: 1 16 java.nio.Bits$1 326: 1 16 java.nio.charset.Charset$ExtendedProviderHolder$1 327: 1 16 java.nio.charset.CoderResult$1 328: 1 16 java.nio.charset.CoderResult$2 329: 1 16 java.security.ProtectionDomain$2 330: 1 16 java.security.ProtectionDomain$JavaSecurityAccessImpl 331: 1 16 java.util.Collections$EmptyIterator 332: 1 16 java.util.Collections$EmptyList 333: 1 16 java.util.Collections$EmptySet 334: 1 16 java.util.Hashtable$EntrySet 335: 1 16 java.util.ResourceBundle$Control 336: 1 16 java.util.ResourceBundle$RBClassLoader$1 337: 1 16 java.util.TimeZone$1 338: 1 16 java.util.WeakHashMap$KeySet 339: 1 16 java.util.concurrent.atomic.AtomicBoolean 340: 1 16 java.util.jar.Attributes 341: 1 16 java.util.jar.JavaUtilJarAccessImpl 342: 1 16 java.util.zip.ZipFile$1 343: 1 16 sun.misc.ASCIICaseInsensitiveComparator 344: 1 16 sun.misc.FloatingDecimal$1 345: 1 16 sun.misc.Launcher 346: 1 16 sun.misc.Launcher$BootClassPathHolder$1 347: 1 16 sun.misc.Launcher$ExtClassLoader$1 348: 1 16 sun.misc.Launcher$Factory 349: 1 16 sun.misc.Perf 350: 1 16 sun.misc.Perf$GetPerfAction 351: 1 16 sun.misc.PostVMInitHook$2 352: 1 16 sun.misc.Unsafe 353: 1 16 sun.net.NetProperties$1 354: 1 16 sun.net.spi.DefaultProxySelector 355: 1 16 sun.net.spi.DefaultProxySelector$1 356: 1 16 sun.net.www.protocol.file.Handler 357: 1 16 sun.reflect.DelegatingMethodAccessorImpl 358: 1 16 sun.reflect.ReflectionFactory 359: 1 16 sun.security.action.GetBooleanAction 360: 1 16 sun.usagetracker.UsageTrackerClient 361: 1 16 sun.util.calendar.Gregorian 362: 1 16 sun.util.calendar.ZoneInfoFile$1 363: 1 16 sun.util.calendar.ZoneInfoFile$Checksum 364: 1 16 sun.util.locale.provider.AuxLocaleProviderAdapter$NullProvider 365: 1 16 sun.util.locale.provider.JRELocaleProviderAdapter$1 366: 1 16 sun.util.locale.provider.SPILocaleProviderAdapter 367: 1 16 sun.util.locale.provider.TimeZoneNameUtility$TimeZoneNameGetter 368: 1 16 sun.util.resources.LocaleData 369: 1 16 sun.util.resources.LocaleData$LocaleDataResourceBundleControl Total 24100 48649392
其他作用
jmap -permstat pid
查看系统的 ClassLoader 信息。
jmap -finalizerinfo pid
查看堆积在 finalizer 队列中的对象。
小结
由于 jmap 将访问堆中的所有对象,为了保证在此过程中不被应用线程干扰,jmap 需要借助安全点机制,让所有线程停留在不改变堆中数据的状态。也就是说,由 jmap 导出的堆快照必定是安全点位置的。这可能导致基于该堆快照的分析结果存在偏差。
举个例子,假设在编译生成的机器码中,某些对象的生命周期在两个安全点之间,那么:live 选项将无法探知到这些对象。
另外,如果某个线程长时间无法跑到安全点,jmap 将一直等下去。与前面讲的 jstat 则不同,垃圾回收器会主动将 jstat 所需要的摘要数据保存至固定位置之中,而 jstat 只需直接读取即可。
jhat:JDK 自带堆分析工具
jhat(JVM Heap Analysis Tool):Sun JDK 提供的 jhat 命令与 jmap 命令搭配使用,用于分析 jmap 生成的 heap dump 文件(堆转储快照)。jhat 内置了一个微型的 HTTP/HTML 服务器,生成 dump 文件的分析结果后,用户可以在浏览器中查看分析结果(分析虚拟机转储快照信息)。
使用了 jhat 命令,就启动了一个 http 服务,端口是 7000,即
http://localhost:7000/
,就可以在浏览器里分析。说明:jhat 命令在 JDK9、JDK10 中已经被删除,官方建议用 VisualVM 代替。
基本适用语法:
jhat
option 参数 作用 -stack false | true 关闭|打开对象分配调用栈跟踪 -refs false | true 关闭|打开对象引用跟踪 -port port-number 设置 jhat HTTP Server 的端口号,默认 7000 -exclude exclude-file 执行对象查询时需要排除的数据成员 -baseline exclude-file 指定一个基准堆转储 -debug int 设置 debug 级别 -version 启动后显示版本信息就退出 -J 传入启动参数,比如-J-Xmx512m 示例
在 jmap 的示例中,我们在 D 盘生成了 5 个 dump 文件,接下来我们直接在命令行使用 jhat 命令,打开 dump 文件。
jhat d:\3.hprof
打开浏览器访问
http://localhost:7000/
。Execute Object Query Language (OQL) query
可以利用 OQL 查询想要的类,如select s from java.lang.String s where s.value.length > 100
jstack:打印 JVM 中线程快照
官方帮助文档:
https://docs.oracle.com/en/java/javase/11/tools/jstack.html
jstack(JVM Stack Trace):用于生成虚拟机指定进程当前时刻的线程快照(虚拟机堆栈跟踪)。线程快照就是当前虚拟机内指定进程的每一条线程正在执行的方法堆栈的集合。
生成线程快照的作用:可用于定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等问题。这些都是导致线程长时间停顿的常见原因。当线程出现停顿时,就可以用 jstack 显示各个线程调用的堆栈情况。
在 thread dump 中,要留意下面几种状态
- 死锁,Deadlock(重点关注)
- 等待资源,Waiting on condition(重点关注)
- 等待获取监视器,Waiting on monitor entry(重点关注)
- 阻塞,Blocked(重点关注)
- 执行中,Runnable
- 暂停,Suspended
- 对象等待中,
Object.wait()
或TIMED_WAITING
- 停止,Parked
option 参数 作用 -F 当正常输出的请求不被响应时,强制输出线程堆栈 -l 除堆栈外,显示关于锁的附加信息 -m 如果调用本地方法的话,可以显示 C/C++ 的堆栈 代码示例(死锁)
public class ThreadDeadLock { public static void main(String[] args) { StringBuilder s1 = new StringBuilder(); StringBuilder s2 = new StringBuilder(); new Thread(){ @Override public void run() { synchronized (s1){ s1.append("a"); s2.append("1"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (s2){ s1.append("b"); s2.append("2"); System.out.println(s1); System.out.println(s2); } } } }.start(); new Thread(new Runnable() { @Override public void run() { synchronized (s2){ s1.append("c"); s2.append("3"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (s1){ s1.append("d"); s2.append("4"); System.out.println(s1); System.out.println(s2); } } } }).start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(new Runnable() { @Override public void run() { Map all = Thread.getAllStackTraces();//追踪当前进程中的所有的线程 Set entries = all.entrySet(); for(Map.Entry en : entries){ Thread t = en.getKey(); StackTraceElement[] v = en.getValue(); System.out.println("【Thread name is :" + t.getName() + "】"); for(StackTraceElement s : v){ System.out.println("\t" + s.toString()); } } } }).start(); } }
运行后,在命令行使用该命令:
D:>jps 8912 22164 ThreadDeadLock 18424 Jps 5084 Launcher D:>jstack 22164
输出的代码:
2022-01-31 21:51:08 Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.231-b11 mixed mode): "DestroyJavaVM" #15 prio=5 os_prio=0 tid=0x0000000002b12800 nid=0x5b50 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Thread-1" #13 prio=5 os_prio=0 tid=0x000000001e041800 nid=0x9bc waiting for monitor entry [0x000000001fd1f000] java.lang.Thread.State: BLOCKED (on object monitor) at com.youngkbt.jstack.ThreadDeadLock$2.run(ThreadDeadLock.java:63) - waiting to lock (a java.lang.StringBuilder) - locked (a java.lang.StringBuilder) at java.lang.Thread.run(Thread.java:748) "Thread-0" #12 prio=5 os_prio=0 tid=0x000000001e03b800 nid=0x52f8 waiting for monitor entry [0x000000001fc1f000] java.lang.Thread.State: BLOCKED (on object monitor) at com.youngkbt.jstack.ThreadDeadLock$1.run(ThreadDeadLock.java:35) - waiting to lock (a java.lang.StringBuilder) - locked (a java.lang.StringBuilder) "Service Thread" #11 daemon prio=9 os_prio=0 tid=0x000000001df8b000 nid=0x3408 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C1 CompilerThread3" #10 daemon prio=9 os_prio=2 tid=0x000000001df47000 nid=0x533c waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x000000001df44800 nid=0x4ef8 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x000000001df42800 nid=0x22b8 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x000000001df40000 nid=0x3494 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x000000001df35800 nid=0x2da8 runnable [0x000000001f4fe000] java.lang.Thread.State: RUNNABLE at java.net.SocketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) at java.net.SocketInputStream.read(SocketInputStream.java:171) at java.net.SocketInputStream.read(SocketInputStream.java:141) at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284) at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326) at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178) - locked (a java.io.InputStreamReader) at java.io.InputStreamReader.read(InputStreamReader.java:184) at java.io.BufferedReader.fill(BufferedReader.java:161) at java.io.BufferedReader.readLine(BufferedReader.java:324) - locked (a java.io.InputStreamReader) at java.io.BufferedReader.readLine(BufferedReader.java:389) at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:47) "Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x000000001de9e000 nid=0xac waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000000001def2000 nid=0x2908 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000001c7c3800 nid=0x1610 in Object.wait() [0x000000001f1df000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144) - locked (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216) "Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x000000001de83000 nid=0x31e4 in Object.wait() [0x000000001f0de000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Object.java:502) at java.lang.ref.Reference.tryHandlePending(Reference.java:191) - locked (a java.lang.ref.Reference$Lock) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153) "VM Thread" os_prio=2 tid=0x000000001de62800 nid=0x575c runnable "GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000002b28800 nid=0x1768 runnable "GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000002b2a000 nid=0x97c runnable "GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000000002b2c000 nid=0x4364 runnable "GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000000002b2d800 nid=0x4608 runnable "GC task thread#4 (ParallelGC)" os_prio=0 tid=0x0000000002b2f800 nid=0x4f38 runnable "GC task thread#5 (ParallelGC)" os_prio=0 tid=0x0000000002b32000 nid=0xb80 runnable "GC task thread#6 (ParallelGC)" os_prio=0 tid=0x0000000002b35000 nid=0xce4 runnable "GC task thread#7 (ParallelGC)" os_prio=0 tid=0x0000000002b36000 nid=0x5510 runnable "GC task thread#8 (ParallelGC)" os_prio=0 tid=0x0000000002b37800 nid=0x193c runnable "GC task thread#9 (ParallelGC)" os_prio=0 tid=0x0000000002b38800 nid=0x1010 runnable "VM Periodic Task Thread" os_prio=2 tid=0x000000001e008000 nid=0x1144 waiting on condition JNI global references: 12 Found one Java-level deadlock: ============================= "Thread-1": waiting to lock monitor 0x000000001e044d58 (object 0x000000076ba1e2f0, a java.lang.StringBuilder), which is held by "Thread-0" "Thread-0": waiting to lock monitor 0x000000001c7c2dc8 (object 0x000000076ba1e338, a java.lang.StringBuilder), which is held by "Thread-1" Java stack information for the threads listed above: =================================================== "Thread-1": at com.youngkbt.jstack.ThreadDeadLock$2.run(ThreadDeadLock.java:63) - waiting to lock (a java.lang.StringBuilder) - locked (a java.lang.StringBuilder) at java.lang.Thread.run(Thread.java:748) "Thread-0": at com.youngkbt.jstack.ThreadDeadLock$1.run(ThreadDeadLock.java:35) - waiting to lock (a java.lang.StringBuilder) - locked (a java.lang.StringBuilder) Found 1 deadlock.
部分图:(可以看出 BLOCKED 进入死锁阻塞)
其他代码示例:
因为内容结果太长,所以只给代码,在命令行的输入可以自行练习查看(也可以使用 option 参数查看额外内容)。
线程睡眠代码:
public class TreadSleepTest { public static void main(String[] args) { System.out.println("hello - 1"); try { Thread.sleep(1000 * 60 * 10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("hello - 2"); } }
线程同步代码:
public class ThreadSyncTest {
public static void main(String[] args) {
Number number = new Number();
Thread t1 = new Thread(number);
Thread t2 = new Thread(number);
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}
class Number implements Runnable {
private int number = 1;
@Override
public void run() {
while (true) {
synchronized (this) {
if (number