主题
JDK命令行工具
标准参数
-help参数
-server -client
-version -showversion
-cp -classpath
x参数
非标准参数
-Xmixed: 混合模式,JVM自己决定是否编译成本地代码(默认)
shell
(base) [root@diyai ~]# java -version
java version "17.0.8" 2023-07-18 LTS
Java(TM) SE Runtime Environment Oracle GraalVM 17.0.8+9.1 (build 17.0.8+9-LTS-jvmci-23.0-b14)
Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 17.0.8+9.1 (build 17.0.8+9-LTS-jvmci-23.0-b14, mixed mode, sharing)
(base) [root@diyai ~]# java -Xmixed -version
java version "17.0.8" 2023-07-18 LTS
Java(TM) SE Runtime Environment Oracle GraalVM 17.0.8+9.1 (build 17.0.8+9-LTS-jvmci-23.0-b14)
Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 17.0.8+9.1 (build 17.0.8+9-LTS-jvmci-23.0-b14, mixed mode, sharing)
-Xint: 解释执行
shell
(base) [root@diyai ~]# java -Xint -version
java version "17.0.8" 2023-07-18 LTS
Java(TM) SE Runtime Environment Oracle GraalVM 17.0.8+9.1 (build 17.0.8+9-LTS-jvmci-23.0-b14)
Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 17.0.8+9.1 (build 17.0.8+9-LTS-jvmci-23.0-b14, interpreted mode, sharing)
-Xcomp: 第一次使用就编译成本地代码
shell
(base) [root@diyai ~]# java -Xcomp -version
java version "17.0.8" 2023-07-18 LTS
Java(TM) SE Runtime Environment Oracle GraalVM 17.0.8+9.1 (build 17.0.8+9-LTS-jvmci-23.0-b14)
Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 17.0.8+9.1 (build 17.0.8+9-LTS-jvmci-23.0-b14, compiled mode, sharing)
xx参数
非标准、相对不稳定、主要用于JVM调优和Debug
1、Boolean类型:+启用,-禁用: 比如 -XX:+UseG1GC
2、非Boolean类型:比如 -XX:GCTimeRatio=19
用于设置垃圾收集时间与总运行时间之间的比率。在HotSpot JVM中,这个参数用来控制并发标记周期的长度。 当值设为19时,这意味着JVM会尝试将垃圾收集工作限制在总运行时间的约5%以内(1/(1+19) ≈ 0.05)。 换句话说,目标是在20秒的应用程序执行时间内,最多只用1秒来进行垃圾收集。
-Xss1024K ,用于设定每个线程的堆栈大小,此处为1024KB,后接单位可为K、M或G
jinfo -flag ThreadStackSize pid
-Xms1024m 等价于-XX:InitialHeapSize ,用于设置初始堆内存大小,比如启动时将至少请求1024MB的堆内存空间
jinfo -flag InitialHeapSize pid
-Xmx1024m 等价于 -XX:MaxHeapSize,用于设置最大堆内存大小
一般建议将两者设置为相同值,可以减少堆内存的动态伸缩带来的碎片化问题,但在某些场景下根据实际需求灵活调整也是必要的。
jinfo -flag MaxHeapSize pid
shell
(base) [root@diyai ~]# jinfo -flag ThreadStackSize 22073
-XX:ThreadStackSize=1024
(base) [root@diyai ~]# jinfo -flag InitialHeapSize 22073
-XX:InitialHeapSize=29360128
(base) [root@diyai ~]# jinfo -flag MaxHeapSize 22073
-XX:MaxHeapSize=461373440
-XX:+PrintFlagInitial
-XX:+PrintFlagFinal
-XX:+UnlockExperimentalVMOptions 解锁实验参数
-XX:+UnlockDiagnosticVMOptions 解锁诊断参数
-XX:+PrintCommandLineFlags 打印命令行参数
= 默认值 := 被用户或JVM修改后的值
shell
(base) [root@diyai ~]# java -XX:+PrintFlagsFinal -version
[Global flags]
int ActiveProcessorCount = -1 {product} {default}
uintx AdaptiveSizeDecrementScaleFactor = 4 {product} {default}
uintx AdaptiveSizeMajorGCDecayTimeScale = 10 {product} {default}
uintx AdaptiveSizePolicyCollectionCostMargin = 50 {product} {default}
uintx AdaptiveSizePolicyInitializingSteps = 20 {product} {default}
uintx AdaptiveSizePolicyOutputInterval = 0 {product} {default}
uintx AdaptiveSizePolicyWeight = 10 {product} {default}
uintx AdaptiveSizeThroughPutPolicy = 0 {product} {default}
uintx AdaptiveTimeWeight = 25 {product} {default}
bool AdjustStackSizeForTLS = false {product} {default}
bool AggressiveHeap = false {product} {default}
intx AliasLevel = 3 {C2 product} {default}
bool AlignVector = false {C2 product} {default}
ccstr AllocateHeapAt = {product} {default}
intx AllocateInstancePrefetchLines = 1 {product} {default}
intx AllocatePrefetchDistance = 192 {product} {default}
intx AllocatePrefetchInstr = 3 {product} {default}
intx AllocatePrefetchLines = 4 {product} {default}
...
统计行数
shell
(base) [root@diyai ~]# java -XX:+PrintFlagsFinal -version |wc -l
java version "17.0.8" 2023-07-18 LTS
Java(TM) SE Runtime Environment Oracle GraalVM 17.0.8+9.1 (build 17.0.8+9-LTS-jvmci-23.0-b14)
Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 17.0.8+9.1 (build 17.0.8+9-LTS-jvmci-23.0-b14, mixed mode, sharing)
575
jps
常用命令
jps -l
jps -v
shell
(base) [root@diyai ~]# jps
13419 wxserver-1.0.0.jar
17708 Jps
(base) [root@diyai ~]# jps -l
17722 jdk.jcmd/sun.tools.jps.Jps
13419 /root/api/wxserver-1.0.0.jar
(base) [root@diyai ~]# jps -q
17736
13419
(base) [root@diyai ~]# jps -m
17750 Jps -m
13419 wxserver-1.0.0.jar
(base) [root@diyai ~]# jps -v
17764 Jps -XX:ThreadPriorityPolicy=1 -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCIProduct -XX:-UnlockExperimentalVMOptions -Dapplication.home=/root/graalvm/graalvm-community-openjdk-17.0.9+9.1 -Xms8m -Djdk.module.main=jdk.jcmd
13419 wxserver-1.0.0.jar -XX:ThreadPriorityPolicy=1 -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCIProduct -XX:-UnlockExperimentalVMOptions -Dspring.profiles.active=prod
(base) [root@diyai ~]# jps -V
17778 Jps
13419 wxserver-1.0.0.jar
jinfo
shell
(base) [root@diyai ~]# jinfo -flags 13419
VM Flags:
-XX:CICompilerCount=2 -XX:+EnableJVMCIProduct -XX:InitialHeapSize=29360128 -XX:MaxHeapSize=461373440 -XX:MaxNewSize=153747456 -XX:MinHeapDeltaBytes=196608 -XX:MinHeapSize=8388608 -XX:NewSize=9764864 -XX:NonNMethodCodeHeapSize=5826188 -XX:NonProfiledCodeHeapSize=122916026 -XX:OldSize=19595264 -XX:ProfiledCodeHeapSize=122916026 -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:SoftMaxHeapSize=461373440 -XX:ThreadPriorityPolicy=1 -XX:-UnlockExperimentalVMOptions -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseSerialGC
查询内存信息
shell
#最大内存
(base) [root@diyai ~]# jinfo -flag MaxHeapSize 13419
-XX:MaxHeapSize=461373440
# 初始内存
(base) [root@diyai ~]# jinfo -flag InitialHeapSize 13419
-XX:InitialHeapSize=29360128
查看垃圾回收器
shell
(base) [root@diyai ~]# jinfo -flag UseG1GC 13419
-XX:-UseG1GC
(base) [root@diyai ~]# jinfo -flag UseSerialGC 13419
-XX:+UseSerialGC
jstat 查看JVM统计信息
包含类加载(-class)、垃圾收集(-gc)、JIT编译(-compiler)
shell
(base) [root@diyai ~]# jstat -help
Usage: jstat --help|-options
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
Definitions:
<option> An option reported by the -options option
<vmid> Virtual Machine Identifier. A vmid takes the following form:
<lvmid>[@<hostname>[:<port>]]
Where <lvmid> is the local vm identifier for the target
Java virtual machine, typically a process id; <hostname> is
the name of the host running the target Java virtual machine;
and <port> is the port number for the rmiregistry on the
target host. See the jvmstat documentation for a more complete
description of the Virtual Machine Identifier.
<lines> Number of samples between header lines.
<interval> Sampling interval. The following forms are allowed:
<n>["ms"|"s"]
Where <n> is an integer and the suffix specifies the units as
milliseconds("ms") or seconds("s"). The default units are "ms".
<count> Number of samples to take before terminating.
-J<flag> Pass <flag> directly to the runtime system.
-? -h --help Prints this help message.
-help Prints this help message.
类加载
在指定时间间隔内类加载与卸载的相关统计信息
shell
(base) [root@diyai ~]# jstat -class 13419 1000 8
Loaded Bytes Unloaded Bytes Time
12769 25027.5 0 0.0 3.79
12769 25027.5 0 0.0 3.79
12769 25027.5 0 0.0 3.79
12769 25027.5 0 0.0 3.79
12769 25027.5 0 0.0 3.79
12769 25027.5 0 0.0 3.79
12769 25027.5 0 0.0 3.79
12769 25027.5 0 0.0 3.79
统计指标 | 数值 | 单位/描述 |
---|---|---|
Loaded | 12769 | 已加载类的数量 |
Bytes | 25027.5 | 已加载类占用的总字节数 |
Unloaded | 0 | 卸载类的数量 |
Bytes | 0.0 | 卸载类所释放的总字节数 |
Time | 3.79 | 自JVM启动以来的时间(秒) |
在jstat -class命令输出中,展示了JVM在指定时间间隔内类加载与卸载的相关统计信息:
- Loaded:表示从上次统计开始到现在新加载的类数量。
- Bytes:已加载类所占用的总内存空间(以字节为单位)。
- Unloaded:在这段时间内被卸载的类的数量。
- Bytes.1:因类卸载而释放的总内存空间(以字节为单位)。
- Time:自JVM启动以来的时间(在这里是3.79秒)。
垃圾收集
shell
(base) [root@diyai ~]# jstat -gc 13419 1000 8
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT CGC CGCT GCT
2560.0 2560.0 0.0 1805.1 20608.0 17451.4 51412.0 42849.6 75328.0 74773.2 8384.0 8102.1 59 0.259 3 0.140 - - 0.398
2560.0 2560.0 0.0 1805.1 20608.0 17451.4 51412.0 42849.6 75328.0 74773.2 8384.0 8102.1 59 0.259 3 0.140 - - 0.398
2560.0 2560.0 0.0 1805.1 20608.0 17451.4 51412.0 42849.6 75328.0 74773.2 8384.0 8102.1 59 0.259 3 0.140 - - 0.398
2560.0 2560.0 0.0 1805.1 20608.0 17451.4 51412.0 42849.6 75328.0 74773.2 8384.0 8102.1 59 0.259 3 0.140 - - 0.398
2560.0 2560.0 0.0 1805.1 20608.0 17451.4 51412.0 42849.6 75328.0 74773.2 8384.0 8102.1 59 0.259 3 0.140 - - 0.398
2560.0 2560.0 0.0 1805.1 20608.0 17451.4 51412.0 42849.6 75328.0 74773.2 8384.0 8102.1 59 0.259 3 0.140 - - 0.398
2560.0 2560.0 0.0 1805.1 20608.0 17451.4 51412.0 42849.6 75328.0 74773.2 8384.0 8102.1 59 0.259 3 0.140 - - 0.398
2560.0 2560.0 0.0 1805.1 20608.0 17451.4 51412.0 42849.6 75328.0 74773.2 8384.0 8102.1 59 0.259 3 0.140 - - 0.398
参数 | 值 | 含义 |
---|---|---|
S0C | 2560.0 | Survivor 0区(Survivor Space 0)当前容量,单位为KB |
S1C | 2560.0 | Survivor 1区(Survivor Space 1)当前容量,单位为KB |
S0U | 0.0 | Survivor 0区当前使用量,单位为KB |
S1U | 1805.1 | Survivor 1区当前使用量,单位为KB |
EC | 20608.0 | Eden区当前容量,单位为KB |
EU | 17451.4 | Eden区当前使用量,单位为KB |
OC | 51412.0 | Old代(老年代)当前容量,单位为KB |
OU | 42849.6 | Old代(老年代)当前使用量,单位为KB |
MC | 75328.0 | 方法区或Metaspace当前容量,单位为KB |
MU | 74773.2 | 方法区或Metaspace当前使用量,单位为KB |
CCSC | 8384.0 | Compressed Class Space(压缩类空间)当前容量,单位为KB |
CCSU | 8102.1 | Compressed Class Space(压缩类空间)当前使用量,单位为KB |
YGC | 59 | 年轻代垃圾回收次数 |
YGCT | 0.259 | 年轻代垃圾回收累计耗时,单位为秒 |
FGC | 3 | Full GC(全局垃圾回收)次数 |
FGCT | 0.140 | Full GC累计耗时,单位为秒 |
CGC | - | 并发标记周期数(CMS并发收集器相关),从输出看该值未被记录或不适用 |
CGCT | - | 并发标记阶段总耗时,单位为秒,同样未显示具体数值 |
GCT | 0.398 | 所有GC操作的总耗时,单位为秒 |
S0C、S1C、S0U、S1U:S0和S1的总量与使用量
EC、EU:Eden区总量与使用量
OC、OU:Old区总量与使用量
MC、MU:Metaspace区总量与使用量
CCSC、CCSU:压缩类空间区总量与使用量
YGC、YGCT:YoungGC的次数与空间
FGC、FGCT:YoungGC的次数与空间
GCT:总的GC时间
JVM的内存结构
CCS 为压缩类空间
JIT编译器
shell
(base) [root@diyai ~]# jstat -compiler 13419
Compiled Failed Invalid Time FailedType FailedMethod
12805 1 0 36.75 1 java/util/concurrent/ConcurrentHashMap put
统计指标 | 数值 | 含义 |
---|---|---|
Compiled | 12805 | 已成功编译的方法总数 |
Failed | 1 | 编译失败的方法数量 |
Invalid | 0 | 失效的已编译方法数量(例如类被卸载或方法删除) |
Time | 36.75 | 自JVM启动以来,用于编译方法所消耗的总时间(秒) |
FailedType | 1 | 最近一次编译失败的方法所属类型编号 |
FailedMethod | java/util/concurrent/ConcurrentHashMap.put | 最近一次编译失败的具体方法及其所在类 |
shell
(base) [root@diyai ~]# jstat -printcompilation 13419
Compiled Size Type Method
12805 1412 1 org/apache/tomcat/util/http/MimeHeaders setValue
统计指标 | 数值 | 方法详情 |
---|---|---|
Compiled | 12805 | 已成功编译的方法总数 |
Size | 1412 | 最近一次被编译方法的字节码大小(字节) |
Type | 1 | 编译类型标识,具体含义取决于JVM实现 |
Method | org/apache/tomcat/util/http/MimeHeaders.setValue | 最近一次被编译的具体方法及其所在类全名 |
实战内存溢出
1、模拟堆内存溢出
java
@Test
public void test() {
List<MPAccessCode> list = new ArrayList<>();
while(true){
list.add(new MPAccessCode().setAccessCode(UUID.randomUUID().toString()));
}
}
设置JVM堆大小 -Xms32M -Xmx32M
shell
java.lang.OutOfMemoryError: Java heap space
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.base/java.lang.Long.fastUUID(Long.java:448)
at java.base/java.lang.System$2.fastUUID(System.java:2316)
at java.base/java.util.UUID.toString(UUID.java:461)
at cn.diyai.utils.TestOOM.test(TestOOM.java:17)
2、模拟MetaSpace溢出
需要创建大量类来消耗Metaspace区域的内存。Metaspace主要存储的是类的元数据信息,如类名、字段描述、方法数据等
1、添加asm依赖
xml
<dependency>
<groupId>asm</groupId>
<artifactId>asm</artifactId>
<version>3.3.1</version>
<scope>test</scope>
</dependency>
2、编写测试代码
java
@Test
public void testMetaSpace(){
List<Class<?>> list = new ArrayList<>();
while(true){
list.addAll(Metaspace.createClasses());
}
}
3、Metaspace类
java
package cn.diyai.utils;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/*
* 继承ClassLoader是为了方便调用defineClass方法,因为该方法的定义为protected
* */
public class Metaspace extends ClassLoader {
public static List<Class<?>> createClasses() {
// 类持有
List<Class<?>> classes = new ArrayList<Class<?>>();
// 循环1000w次生成1000w个不同的类。
for (int i = 0; i < 10000000; ++i) {
ClassWriter cw = new ClassWriter(0);
// 定义一个类名称为Class{i},它的访问域为public,父类为java.lang.Object,不实现任何接口
cw.visit(Opcodes.V1_1, Opcodes.ACC_PUBLIC, "Class" + i, null,
"java/lang/Object", null);
// 定义构造函数<init>方法
MethodVisitor mw = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>",
"()V", null, null);
// 第一个指令为加载this
mw.visitVarInsn(Opcodes.ALOAD, 0);
// 第二个指令为调用父类Object的构造函数
mw.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object",
"<init>", "()V", false);
// 第三条指令为return
mw.visitInsn(Opcodes.RETURN);
mw.visitMaxs(1, 1);
mw.visitEnd();
Metaspace test = new Metaspace();
byte[] code = cw.toByteArray();
// 定义类
Class<?> exampleClass = test.defineClass("Class" + i, code, 0, code.length);
classes.add(exampleClass);
}
return classes;
}
}
4、设置JVM参数,设置最大元空间大小10M
-XX:MaxMetaspaceSize=10M
java
Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
at java.base/java.lang.ClassLoader.defineClass1(Native Method)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1012)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:874)
at cn.diyai.utils.Metaspace.createClasses(Metaspace.java:38)
at cn.diyai.utils.TestOOM.testMetaSpace(TestOOM.java:33)
导出内存映像
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./
加入以上JVM参数再运行 模拟堆内存溢出,执行结果
java
java.lang.OutOfMemoryError: Java heap space
Dumping heap to ./\java_pid8340.hprof ...
Heap dump file created [46044113 bytes in 0.072 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.base/java.lang.Long.fastUUID(Long.java:448)
at java.base/java.lang.System$2.fastUUID(System.java:2316)
at java.base/java.util.UUID.toString(UUID.java:461)
at cn.diyai.utils.TestOOM.test(TestOOM.java:17)
此时会在指定目录下生成一个hprof结尾的映像文件
jmap
shell
# jmap -help
Usage:
jmap -clstats <pid>
to connect to running process and print class loader statistics
jmap -finalizerinfo <pid>
to connect to running process and print information on objects awaiting finalization
jmap -histo[:[<histo-options>]] <pid>
to connect to running process and print histogram of java object heap
jmap -dump:<dump-options> <pid>
to connect to running process and dump java heap
jmap -? -h --help
to print this help message
dump-options:
live dump only live objects (takes precedence if both "live" and "all" are specified)
all dump all objects in the heap (default if one of "live" or "all" is not specified)
format=b binary format
file=<file> dump heap to <file>
gz=<number> If specified, the heap dump is written in gzipped format using the given compression level.
1 (recommended) is the fastest, 9 the strongest compression.
Example: jmap -dump:live,format=b,file=heap.bin <pid>
histo-options:
live count only live objects (takes precedence if both "live" and "all" are specified)
all count all objects in the heap (default if one of "live" or "all" is not specified)
file=<file> dump data to <file>
parallel=<number> Number of parallel threads to use for heap inspection:
0 (the default) means let the VM determine the number of threads to use
1 means use one thread (disable parallelism).
For any other value the VM will try to use the specified number of threads, but might use fewer.
Example: jmap -histo:live,file=/tmp/histo.data <pid>
-heap
shell
(base) [root@diyai ~]# jmap -dump:format=b,file=./dumps/heap.hprof 13419
Dumping heap to /root/dumps/heap.hprof ...
Heap dump file created [102437122 bytes in 0.370 secs]
-clstats
-dump
shell
(base) [root@diyai ~]# jhsdb jmap --pid 13419
Attaching to process ID 13419, please wait...
WARNING: Hotspot VM version 17.0.8+9-LTS-jvmci-23.0-b14 does not match with SA version 17.0.9+9-jvmci-23.0-b22. You may see unexpected results.
Debugger attached successfully.
Server compiler detected.
JVM version is 17.0.8+9-LTS-jvmci-23.0-b14
0x000055c2767ef000 16K /root/Java/graalvm-jdk-17/bin/java
0x00007fc9c5215000 47020K /root/Java/graalvm-jdk-17/lib/libjvmcicompiler.so
0x00007fca669ff000 32K /root/Java/graalvm-jdk-17/lib/libmanagement_ext.so
0x00007fca66a07000 64K /root/Java/graalvm-jdk-17/lib/libverify.so
0x00007fca67825000 832K /root/Java/graalvm-jdk-17/lib/libjsvml.so
0x00007fca67d17000 2980K /usr/lib64/libnss_files-2.17.so
0x00007fca67f2a000 4952K /usr/lib64/libm-2.17.so
0x00007fca6822c000 3920K /usr/lib64/librt-2.17.so
0x00007fca68434000 18868K /root/Java/graalvm-jdk-17/lib/server/libjvm.so
0x00007fca696a1000 5500K /usr/lib64/libc-2.17.so
0x00007fca69a6f000 3652K /usr/lib64/libdl-2.17.so
0x00007fca69c73000 3636K /usr/lib64/libpthread-2.17.so
0x00007fca69e8f000 3524K /usr/lib64/libz.so.1.2.7
0x00007fca6a0a5000 3436K /usr/lib64/ld-2.17.so
0x00007fca6a0cd000 24K /root/Java/graalvm-jdk-17/lib/libmanagement.so
0x00007fca6a114000 40K /root/Java/graalvm-jdk-17/lib/libzip.so
0x00007fca6a11e000 96K /root/Java/graalvm-jdk-17/lib/libnet.so
0x00007fca6a136000 84K /root/Java/graalvm-jdk-17/lib/libnio.so
0x00007fca6a157000 160K /root/Java/graalvm-jdk-17/lib/libjava.so
0x00007fca6a187000 124K /root/Java/graalvm-jdk-17/lib/libjimage.so
0x00007fca6a2ab000 68K /root/Java/graalvm-jdk-17/lib/libjli.so
0x00007fca6a2be000 16K /root/Java/graalvm-jdk-17/lib/libextnet.so
Mat
解压缩后打开模拟堆内存溢出生成的java_pid8340.hprof,可见mat已经识别出内存溢出的代码为test函数

Histogram
右击cn.diyai.model.MPAccessCode
-> Merge Shortest Paths to GC Roots -> exclude all phantom/weak/soft etc.references
查看GC Roots
查看对象数量
查看对象所占的内存
jstack
shell
(base) [root@diyai ~]# jstack 13419 > dumps/jstack.txt
(base) [root@diyai ~]# cat dumps/jstack.txt
2024-02-11 09:56:01
Full thread dump Java HotSpot(TM) 64-Bit Server VM (17.0.8+9-LTS-jvmci-23.0-b14 mixed mode, sharing):
Threads class SMR info:
_java_thread_list=0x00007fc9d0012f00, length=34, elements={
0x00007fca6005e0c0, 0x00007fca6005f6f0, 0x00007fca60066210, 0x00007fca600676d0,
0x00007fca60068bc0, 0x00007fca6006a6f0, 0x00007fca6006bd20, 0x00007fca6006d2c0,
0x00007fca60099bd0, 0x00007fca600fb100, 0x00007fca60c55e50, 0x00007fca60c568a0,
0x00007fca60c57890, 0x00007fca60c71540, 0x00007fca60e2ba60, 0x00007fca6105ebd0,
0x00007fca6106c5a0, 0x00007fca612d5930, 0x00007fca612d6aa0, 0x00007fca612d7bf0,
0x00007fca612db6d0, 0x00007fca612dc5d0, 0x00007fca612ddad0, 0x00007fca612defb0,
0x00007fca612e04a0, 0x00007fca612e1990, 0x00007fca612e2e90, 0x00007fca612e4ee0,
0x00007fca612e6fb0, 0x00007fc1b8053fa0, 0x00007fc1b8055af0, 0x00007fca600236f0,
0x00007fc9f411bdd0, 0x00007fc9e8021700
}
"Reference Handler" #2 daemon prio=10 os_prio=-5 cpu=3.72ms elapsed=157434.90s tid=0x00007fca6005e0c0 nid=0x346e waiting on condition [0x00007fca67721000]
java.lang.Thread.State: RUNNABLE
at java.lang.ref.Reference.waitForReferencePendingList(java.base@17.0.8/Native Method)
at java.lang.ref.Reference.processPendingReferences(java.base@17.0.8/Reference.java:253)
at java.lang.ref.Reference$ReferenceHandler.run(java.base@17.0.8/Reference.java:215)
以上输出的含义
属性 | 描述 | 说明 |
---|---|---|
线程名称 | "Reference Handler" #2 daemon | |
线程的优先级 | prio=10 | 在Java线程中,优先级10是最高的 |
操作系统优先级 | os_prio=-5 | 表示操作系统层面赋予该线程的优先级,其值可能因操作系统而异,负值通常意味着这是一个调整过的相对优先级 |
CPU占用时间 | cpu=3.72ms | 线程自启动以来占用CPU的时间总计 |
运行时长 | elapsed=157434.90s | 该线程已经运行了大约1天21小时57分钟(约157434.90秒) |
线程ID | tid=0x00007fca6005e0c0 | |
Native线程ID | nid=0x346e | |
内存地址 | [0x00007fca67721000] | |
线程状态 | java.lang.Thread.State: RUNNABLE | 当前线程状态 |
Thread State | Description |
---|---|
NEW | The thread has not yet started. |
RUNNABLE | The thread is executing in the JVM. |
BLOCKED | The thread is blocked waiting for a monitor lock. |
WAITING | The thread is waiting indefinitely for another thread to perform a particular action. |
TIMED_WAITING | The thread is waiting for another thread to perform an action for up to a specified waiting time. |
TERMINATED | The thread has exited. |
线程状态转换
实战排查死循环导致CPU飙升
CPU飙升问题
java
@RestController
@RequestMapping("/jvmtest")
public class LoopController {
@RequestMapping("/loopCpu")
public String loopCpu(){
while(true){
System.out.println("cpu");
}
}
}
当访问http://localhost:xx/jvmtest/loopCpu
时,会发现服务响应慢,top一下发现20443占用的cpu上升已达到97.3%
shell
top - 14:42:42 up 50 days, 15:24, 2 users, load average: 0.77, 0.28, 0.14
Tasks: 104 total, 1 running, 103 sleeping, 0 stopped, 0 zombie
%Cpu(s): 18.5 us, 31.6 sy, 0.0 ni, 43.2 id, 6.6 wa, 0.0 hi, 0.2 si, 0.0 st
KiB Mem : 1798504 total, 81556 free, 930860 used, 786088 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 706080 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
20443 root 20 0 35.1g 236628 33492 S 97.3 13.2 0:51.35 java
dump stack
shell
(base) [root@diyai ~]# jstack 20443 > dumps/jstack20443.txt
查看进程对应的线程
top -p 20443 -H
shell
top - 14:43:33 up 50 days, 15:24, 2 users, load average: 0.90, 0.39, 0.18
Threads: 36 total, 1 running, 35 sleeping, 0 stopped, 0 zombie
%Cpu(s): 19.3 us, 31.6 sy, 0.0 ni, 48.9 id, 0.2 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 1798504 total, 67504 free, 927184 used, 803816 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 709428 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
20489 root 20 0 3217896 229176 30112 R 99.7 12.7 1:26.46 https-jsse-nio-
可发现20489占CPU过高,将pid转成16进制后,从dumps/jstack20443.txt定位到执行的代码
shell
(base) [root@diyai ~]# printf "%x" 20489
5009
dump日志为
java
"https-jsse-nio-443-exec-2" #24 daemon prio=5 os_prio=0 cpu=273910.23ms elapsed=298.40s tid=0x00007f58c51e4fb0 nid=0x5009 runnable [0x00007f58c99e4000]
java.lang.Thread.State: RUNNABLE
at java.io.FileOutputStream.writeBytes(java.base@17.0.8/Native Method)
at java.io.FileOutputStream.write(java.base@17.0.8/FileOutputStream.java:349)
at java.io.BufferedOutputStream.flushBuffer(java.base@17.0.8/BufferedOutputStream.java:81)
at java.io.BufferedOutputStream.flush(java.base@17.0.8/BufferedOutputStream.java:142)
- locked <0x00000000edf9ea38> (a java.io.BufferedOutputStream)
at java.io.PrintStream.write(java.base@17.0.8/PrintStream.java:570)
- locked <0x00000000edf673c0> (a java.io.PrintStream)
at sun.nio.cs.StreamEncoder.writeBytes(java.base@17.0.8/StreamEncoder.java:234)
at sun.nio.cs.StreamEncoder.implFlushBuffer(java.base@17.0.8/StreamEncoder.java:313)
at sun.nio.cs.StreamEncoder.flushBuffer(java.base@17.0.8/StreamEncoder.java:111)
- locked <0x00000000edf9ea90> (a java.io.OutputStreamWriter)
at java.io.OutputStreamWriter.flushBuffer(java.base@17.0.8/OutputStreamWriter.java:178)
at java.io.PrintStream.writeln(java.base@17.0.8/PrintStream.java:723)
- locked <0x00000000edf673c0> (a java.io.PrintStream)
at java.io.PrintStream.println(java.base@17.0.8/PrintStream.java:1028)
at cn.diyai.controller.LoopController.loopCpu(LoopController.java:13)
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(java.base@17.0.8/Native Method)
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(java.base@17.0.8/NativeMethodAccessorImpl.java:77)
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(java.base@17.0.8/DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(java.base@17.0.8/Method.java:568)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1071)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:964)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:670)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:779)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1789)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
- locked <0x00000000e5f77508> (a org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(java.base@17.0.8/Thread.java:833)
CPU负载高有可能是任务太多CPU处理不过来,也有可能是存在等待的操作比如读写磁盘之类的io操作,需要具体问题具体分析。
死锁问题
java
@RequestMapping("deadLock")
public String deadLock(){
new Thread(()->{
synchronized (lock1){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
log.error("thread1 sleep interrupt",e);
}
synchronized (lock2){
System.out.println("任务1执行完成");
}
}
}).start();
new Thread(()->{
synchronized (lock2){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
log.error("thread2 sleep interrupt",e);
}
synchronized (lock1){
log.info("任务2执行完成");
}
}
}).start();
return "deadLock";
}
访问后xxxx/jvmtest/deadLock,dump线程栈
(base) [root@diyai ~]# jstack 21273 > dumps/21273.txt
(base) [root@diyai ~]# cat dumps/21273.txt
shell
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x00007fab58001050 (object 0x00000000ef2151b0, a java.lang.Object),
which is held by "Thread-2"
"Thread-2":
waiting to lock monitor 0x00007fab64028580 (object 0x00000000ef2151a0, a java.lang.Object),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at cn.diyai.controller.LoopController.lambda$deadLock$0(LoopController.java:32)
- waiting to lock <0x00000000ef2151b0> (a java.lang.Object)
- locked <0x00000000ef2151a0> (a java.lang.Object)
at cn.diyai.controller.LoopController$$Lambda$1106/0x00007fab96724030.run(Unknown Source)
at java.lang.Thread.run(java.base@17.0.8/Thread.java:833)
"Thread-2":
at cn.diyai.controller.LoopController.lambda$deadLock$1(LoopController.java:45)
- waiting to lock <0x00000000ef2151a0> (a java.lang.Object)
- locked <0x00000000ef2151b0> (a java.lang.Object)
at cn.diyai.controller.LoopController$$Lambda$1107/0x00007fab96724258.run(Unknown Source)
at java.lang.Thread.run(java.base@17.0.8/Thread.java:833)
Found 1 deadlock.
可发现有死锁问题