jvm监控与调优之jdk命令行工具
目录
jvm的参数类型
标准参数
- -help
- -server -client
- -version -showversion
- -cp -classpath
X参数
- 非标准化参数(在各个JDK版本中可能会变,但是变动比较小)
- -Xint : 解释执行
//使用命令:java -Xint -version jvm为interpreted mode(解释执行)
C:\Users\jeffrey>java -Xint -version
java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, interpreted mode)
- -Xcomp : 编译执行 第一次使用就编译成本地代码
//使用命令:java -Xcomp -version jvm为compiled mode(编译执行)
C:\Users\jeffrey>java -Xcomp -version
java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, compiled mode)
- -Xmixed :混合模式,JVM自己来决定是否编译成本地代码
//使用命令:java -version jvm为mixed mode(混合模式)
C:\Users\jeffrey>java -version
java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)
XX参数
- 非标转化参数
- 相对不稳定
- 主要用于JVM调优和Debug
- 参数分类
- Boolean 类型
//格式:-XX:[+-]<name> 表示启用或禁用name属性 //比如: -XX:+UseConcMarkSweepGC //表示启用CMS垃圾收集器([+]号代表启用,[-]代表禁用) -XX:+UseGcG1 //表示启用G1垃圾收集器
- 非 Boolean 类型
//格式:-XX:<name>=<value> 表示name属性的值是value //比如: -XX:MaxGcPauseMillis=500 //GC的最大停顿时间是500毫秒 -XX:GCTimeRatio=19 //设置吞吐量大小,它的值是一个 0-100 之间的整数。假设 GCTimeRatio 的值为 n,那么系统将花费不超过 1/(1+n) 的时间用于垃圾收集
- Boolean 类型
-Xmx -Xms
- 不是X参数,而是XX参数
- -Xms 等价于 -XX:InitialHeapSize 初始化的堆大小
- -Xmx 等价于 -XX:MaxHeapSize 最大化的堆大小
- jinfo -flag MaxHeapSize <进程编号>
//查询所有java进程 [root@localhost ~]# ps -ef | grep java root 13220 9664 9 16:24 pts/1 00:00:08 java -jar spring-boot-test-0.0.1-SNAPSHOT.jar [root@localhost ~]# jinfo -flag MaxHeapSize 13220 -XX:MaxHeapSize=515899392 //运行时最大的堆大小
- jinfo -flag ThreadStackSize <进程编号>
[root@localhost ~]# ps -ef | grep java root 13220 9664 9 16:24 pts/1 00:00:08 java -jar spring-boot-test-0.0.1-SNAPSHOT.jar [root@localhost ~]# jinfo -flag ThreadStackSize 13220 -XX:ThreadStackSize=1024K //启动一个线程需要的内存大小
运行时jvm参数查看
运行时参数
- -XX:+PrintFlagsInitial
- -XX:PrintFlagsFinal
- -XX:+UnlockExperimentalVMOptions 解锁实验参数
- -XX:+UnlockDiagnosticVMOptions 解锁诊断参数
- -XX:+PrintCommandLineFlags 打印命令行参数
//=表示默认值
//:被用户或者JVM修改过后的值
[root@localhost ~]# java -XX:+PrintFlagsFinal -version
[Global flags]
intx ActiveProcessorCount = -1 {product}
uintx AdaptiveSizeDecrementScaleFactor = 4 {product}
uintx MaxHeapFreeRatio = 100 {manageable}
uintx MaxHeapSize := 515899392 {product}
uintx MaxMetaspaceExpansion = 5451776 {product}
uintx MaxMetaspaceFreeRatio = 70 {product}
uintx MaxMetaspaceSize = 18446744073709547520 {product}
uintx MaxNewSize := 171966464 {product}
uintx MaxRAMFraction = 4 {product}
uintx MaxTenuringThreshold = 15 {product}
uintx MinHeapDeltaBytes := 524288 {product}
uintx MinHeapFreeRatio = 0 {manageable}
//文件下载到本地查看
[root@localhost ~]# java -XX:+PrintFlagsFinal -version > falgs.txt
java version "1.8.0_211"
Java(TM) SE Runtime Environment (build 1.8.0_211-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.211-b12, mixed mode)
[root@localhost ~]# sz falgs.txt
jps
用来查看java进程,参考网站:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jps.html#CHDCGECD
[root@localhost ~]# jps -l
17089 sun.tools.jps.Jps
7331 /usr/lib/jenkins/jenkins.war
13220 spring-boot-test-0.0.1-SNAPSHOT.jar
jinfo
jinfo是jdk自带的命令,可以用来查看正在运行的Java应用程序的扩展参数,甚至支持在运行时,修改部分参数。
官网地址:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jinfo.html#BCGEBFDD
//查看最大内存
[root@localhost ~]# jinfo -flag MaxHeapSize 13220
-XX:MaxHeapSize=515899392
//查看设置过值的参数
[root@localhost ~]# jinfo -flags 13220
Attaching to process ID 13220, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.211-b12
Non-default VM flags: -XX:CICompilerCount=2 -XX:InitialHeapSize=33554432 -XX:MaxHeapSize=515899392 -XX:MaxNewSize=171966464 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=11010048 -XX:OldSize=22544384 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC
Command line:
//查看垃圾回收器
[root@localhost ~]# jinfo -flag UseConcMarkSweepGC 13220
-XX:-UseConcMarkSweepGC
[root@localhost ~]# jinfo -flag UseG1GC 13220
-XX:-UseG1GC
[root@localhost ~]# jinfo -flag UseParallelGC 13220
-XX:+UseParallelGC
jstat查看虚拟机统计信息
概述
Jstat是JDK自带的一个轻量级小工具。全称“Java Virtual Machine statistics monitoring tool”,它位于java的bin目录下,主要利用JVM内建的指令对Java应用程序的资源和性能进行实时的命令行的监控,包括了对Heap size和垃圾回收状况的监控。
jstat 用法
[root@localhost ~]# jstat -help
Usage: jstat -help|-options
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
option: 参数选项
-t: 可以在打印的列加上Timestamp列,用于显示系统运行的时间
-h: 可以在周期性数据数据的时候,可以在指定输出多少行以后输出一次表头
vmid: Virtual Machine ID( 进程的 pid)
interval: 执行每次的间隔时间,单位为毫秒
count: 用于指定输出多少次记录,缺省则会一直打印
[root@localhost ~]# jstat -options
-class
-compiler
-gc
-gccapacity
-gccause
-gcmetacapacity
-gcnew
-gcnewcapacity
-gcold
-gcoldcapacity
-gcutil
-printcompilation
option:可以从下面参数中选择
-class 显示ClassLoad的相关信息;
-compiler 显示JIT编译的相关信息;
-gc 显示和gc相关的堆信息;
-gccapacity 显示各个代的容量以及使用情况;
-gcmetacapacity 显示metaspace的大小;
-gcnew 显示新生代信息;
-gcnewcapacity 显示新生代大小和使用情况;
-gcold 显示老年代和永久代的信息;
-gcoldcapacity 显示老年代的大小;
-gcutil 显示垃圾收集信息;
-gccause 显示垃圾回收的相关信息(通-gcutil),同时显示最后一次或当前正在发生的垃圾回收的诱因;
-printcompilation 输出JIT编译的方法信息;
示例一:-class
显示加载class的数量,及所占空间等信息。
//语法:jstat -class <pid>
[root@localhost ~]# jstat -class 13220
Loaded | Bytes | Unloaded | Bytes | Time |
---|---|---|---|---|
5354 | 10042.1 | 0 | 0.0 | 5.53 |
Loaded : 已经装载的类的数量
Bytes : 装载类所占用的字节数
Unloaded:已经卸载类的数量
Bytes:卸载类的字节数
Time:装载和卸载类所花费的时间
示例二:-compiler
显示VM实时编译(JIT)的数量等信息。
//语法:jstat -compiler <pid>
[root@localhost ~]# jstat -compiler 13220
Compiled | Failed | Invalid | Time | FailedType | FailedMethod |
---|---|---|---|---|---|
3035 | 1 | 0 | 6.72 | 1 | org/springframework/boot/loader/jar/JarURLConnection get |
Compiled:编译任务执行数量
Failed:编译任务执行失败数量
Invalid :编译任务执行失效数量
Time :编译任务消耗时间
FailedType:最后一个编译失败任务的类型
FailedMethod:最后一个编译失败任务所在的类及方法
示例三: -gc
显示gc相关的堆信息,查看gc的次数,及时间。
//语法:jstat –gc <pid>
[root@localhost ~]# jstat -gc 13220
S0C | S1C | S0U | S1U | EC | EU | OC | OU | MC | MU | CCSC | CCSU | YGC | YGCT | FGC | FGCT | GCT |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
6656.0 | 5120.0 | 0.0 | 5111.7 | 101376.0 | 72302.8 | 24576.0 | 16123.4 | 27776.0 | 27199.6 | 3456.0 | 3349.7 | 11 | 0.097 | 1 | 0.111 | 0.208 |
S0C:年轻代中第一个survivor(幸存区)的容量 (字节)
S1C:年轻代中第二个survivor(幸存区)的容量 (字节)
S0U:年轻代中第一个survivor(幸存区)目前已使用空间 (字节)
S1U:年轻代中第二个survivor(幸存区)目前已使用空间 (字节)
EC:年轻代中Eden(伊甸园)的容量 (字节)
EU:年轻代中Eden(伊甸园)目前已使用空间 (字节)
OC:Old代的容量 (字节)
OU:Old代目前已使用空间 (字节)
MC:metaspace(元空间)的容量 (字节)
MU:metaspace(元空间)目前已使用空间 (字节)
YGC:从应用程序启动到采样时年轻代中gc次数
YGCT:从应用程序启动到采样时年轻代中gc所用时间(s)
FGC:从应用程序启动到采样时old代(全gc)gc次数
FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s)
GCT:从应用程序启动到采样时gc用的总时间(s)
示例四: -gccapacity
可以显示,VM内存中三代(young,old,perm)对象的使用和占用大小
//语法:jstat -gccapacity <pid>
[root@localhost ~]# jstat -gccapacity 13220
NGCMN | NGCMX | NGC | S0C | S1C | EC | OGCMN | OGCMX | OGC | OC | MCMN | MCMX | MC | CCSMN | CCSMX | CCSC | YGC | FGC |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
10752.0 | 167936. | 117760.0 | 6656.0 | 5120.0 | 101376.0 | 22016.0 | 335872.0 | 24576.0 | 24576.0 | 0.0 | 1073152.0 | 27776.0 | 0.0 | 1048576.0 | 3456.0 | 11 | 1 |
NGCMN:年轻代(young)中初始化(最小)的大小(字节)
NGCMX:年轻代(young)的最大容量 (字节)
NGC:年轻代(young)中当前的容量 (字节)
S0C:年轻代中第一个survivor(幸存区)的容量 (字节)
S1C: 年轻代中第二个survivor(幸存区)的容量 (字节)
EC:年轻代中Eden(伊甸园)的容量 (字节)
OGCMN:old代中初始化(最小)的大小 (字节)
OGCMX:old代的最大容量(字节)
OGC:old代当前新生成的容量 (字节)
OC:Old代的容量 (字节)
MCMN:metaspace(元空间)中初始化(最小)的大小 (字节)
MCMX:metaspace(元空间)的最大容量 (字节)
MC:metaspace(元空间)当前新生成的容量 (字节)
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:从应用程序启动到采样时年轻代中gc次数
FGC:从应用程序启动到采样时old代(全gc)gc次数
示例五:-gcmetacapacity
metaspace 中对象的信息及其占用量。
//语法:jstat -gcmetacapacity <pid>
[root@localhost ~]# jstat -gcmetacapacity 13220
MCMN | MCMX | MC | CCSMN | CCSMX | CCSC | YGC | FGC | FGCT | GCT |
---|---|---|---|---|---|---|---|---|---|
0.0 | 1073152.0 | 27776.0 | 0.0 | 1048576.0 | 3456.0 | 11 | 1 | 0.111 | 0.208 |
MCMN:最小元数据容量
MCMX:最大元数据容量
MC:当前元数据空间大小
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:从应用程序启动到采样时年轻代中gc次数
FGC:从应用程序启动到采样时old代(全gc)gc次数
FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s)
GCT:从应用程序启动到采样时gc用的总时间(s)
示例六: -gcnew
年轻代对象的信息。
//语法:jstat -gcnew <pid>
[root@localhost ~]# jstat -gcnew 13220
S0C | S1C | S0U | S1U | TT | MTT | DSS | EC | EU | YGC | YGCT |
---|---|---|---|---|---|---|---|---|---|---|
6656.0 | 5120.0 | 0.0 | 5111.7 | 2 | 15 | 8192.0 | 101376.0 | 72302.8 | 11 | 0.097 |
S0C:年轻代中第一个survivor(幸存区)的容量 (字节)
S1C:年轻代中第二个survivor(幸存区)的容量 (字节)
S0U:年轻代中第一个survivor(幸存区)目前已使用空间 (字节)
S1U:年轻代中第二个survivor(幸存区)目前已使用空间 (字节)
TT:持有次数限制
MTT:最大持有次数限制
DSS:期望的幸存区大小
EC:年轻代中Eden(伊甸园)的容量 (字节)
EU:年轻代中Eden(伊甸园)目前已使用空间 (字节)
YGC:从应用程序启动到采样时年轻代中gc次数
YGCT:从应用程序启动到采样时年轻代中gc所用时间(s)
示例七: -gcnewcapacity
年轻代对象的信息及其占用量
//语法:jstat -gcnewcapacity <pid>
[root@localhost ~]# jstat -gcnewcapacity 13220
NGCMN | NGCMX | NGC | S0CMX | S0C | S1CMX | S1C | ECMX | EC | YGC | FGC |
---|---|---|---|---|---|---|---|---|---|---|
10752.0 | 167936.0 | 117760.0 | 55808.0 | 6656.0 | 55808.0 | 5120.0 | 166912.0 | 101376.0 | 11 | 1 |
NGCMN:年轻代(young)中初始化(最小)的大小(字节)
NGCMX:年轻代(young)的最大容量 (字节)
NGC:年轻代(young)中当前的容量 (字节)
S0CMX:年轻代中第一个survivor(幸存区)的最大容量 (字节)
S0C:年轻代中第一个survivor(幸存区)的容量 (字节)
S1CMX:年轻代中第二个survivor(幸存区)的最大容量 (字节)
S1C:年轻代中第二个survivor(幸存区)的容量 (字节)
ECMX:年轻代中Eden(伊甸园)的最大容量 (字节)
EC:年轻代中Eden(伊甸园)的容量 (字节)
YGC:从应用程序启动到采样时年轻代中gc次数
FGC:从应用程序启动到采样时old代(全gc)gc次数
示例八: -gcold
old代对象的信息
//语法:jstat -gcold <pid>
[root@localhost ~]# jstat -gcold 13220
MC | MU | CCSC | CCSU | OC | OU | YGC | FGC | FGCT | GCT |
---|---|---|---|---|---|---|---|---|---|
27776.0 | 27199.6 | 3456.0 | 3349.7 | 24576.0 | 16123.4 | 11 | 1 | 0.111 | 0.208 |
MC :metaspace(元空间)的容量 (字节)
MU:metaspace(元空间)目前已使用空间 (字节)
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
OC:Old代的容量 (字节)
OU:Old代目前已使用空间 (字节)
YGC:从应用程序启动到采样时年轻代中gc次数
FGC:从应用程序启动到采样时old代(全gc)gc次数
FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s)
GCT:从应用程序启动到采样时gc用的总时间(s)
示例九:-gcoldcapacity
old代对象的信息及其占用量
//语法:jstat -gcoldcapacity <pid>
[root@localhost ~]# jstat -gcoldcapacity 13220
OGCMN | OGCMX | OGC | OC | YGC | FGC | FGCT | GCT |
---|---|---|---|---|---|---|---|
22016.0 | 335872.0 | 24576.0 | 24576.0 | 11 | 1 | 0.111 | 0.208 |
示例十: - gcutil
统计gc信息
//语法:jstat -gcutil <pid>
[root@localhost ~]# jstat -gcutil 13220
S0 | S1 | E | O | M | CCS | YGC | YGCT | FGC | FGCT | GCT |
---|---|---|---|---|---|---|---|---|---|---|
0.00 | 99.84 | 72.49 | 65.61 | 97.92 | 96.92 | 11 | 0.097 | 1 | 0.111 | 0.208 |
S0:年轻代中第一个survivor(幸存区)已使用的占当前容量百分比
S1:年轻代中第二个survivor(幸存区)已使用的占当前容量百分比
E:年轻代中Eden(伊甸园)已使用的占当前容量百分比
O:old代已使用的占当前容量百分比
P:perm代已使用的占当前容量百分比
YGC:从应用程序启动到采样时年轻代中gc次数
YGCT:从应用程序启动到采样时年轻代中gc所用时间(s)
FGC:从应用程序启动到采样时old代(全gc)gc次数
FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s)
GCT:从应用程序启动到采样时gc用的总时间(s)
示例十一:-gccause
显示垃圾回收的相关信息(通-gcutil),同时显示最后一次或当前正在发生的垃圾回收的诱因。
//语法:jstat -gccause <pid>
[root@localhost ~]# jstat -gccause 13220
S0 | S1 | E | O | M | CCS | YGC | YGCT | FGC | FGCT | GCT | LGCC | GCC |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0.00 | 99.84 | 72.49 | 65.61 | 97.92 | 96.92 | 11 | 0.097 | 1 | 0.111 | 0.208 | Allocation Failure | No GC |
S0:年轻代中第一个survivor(幸存区)已使用的占当前容量百分比
S1:年轻代中第二个survivor(幸存区)已使用的占当前容量百分比
E:年轻代中Eden(伊甸园)已使用的占当前容量百分比
O:old代已使用的占当前容量百分比
P:perm代已使用的占当前容量百分比
YGC:从应用程序启动到采样时年轻代中gc次数
YGCT:从应用程序启动到采样时年轻代中gc所用时间(s)
FGC:从应用程序启动到采样时old代(全gc)gc次数
FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s)
GCT:从应用程序启动到采样时gc用的总时间(s)
LGCC:最后一次GC原因
GCC:当前GC原因(No GC 为当前没有执行GC)
示例十二: -printcompilation
当前VM执行的信息。
//语法:jstat -printcompilation <pid>
[root@localhost ~]# jstat -printcompilation 13220
Compiled | Size | Type | Method |
---|---|---|---|
3071 | 528 | 1 | org/apache/catalina/session/ManagerBase processExpires |
Compiled :编译任务的数目
Size :方法生成的字节码的大小
Type:编译类型
Method:类名和方法名用来标识编译的方法。类名使用/做为一个命名空间分隔符。方法名是给定类中的方法。上述格式是由-XX:+PrintComplation选项进行设置的
jmap + MAT实战内存溢出
一、构建程序溢出代码
1. Controller
package com.ceair;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@RestController
public class MemoryController {
private List<User> userList = new ArrayList<User>();
private List<Class<?>> classList = new ArrayList<Class<?>>();
/**
* 堆内存溢出
* VM参数:-Xms16m -Xmx16m
* */
@GetMapping("/heap")
public void Test() {
while (true) {
int i = 0;
userList.add(new User(++i, UUID.randomUUID().toString()));
}
}
/**
* VM参数:-XX:MetaspaceSize=16M -XX:MaxMetaspaceSize=16M
*/
@GetMapping("/nonheap")
public void nonheap() {
while (true) {
classList.addAll(Metaspace.CreateClasses());
}
}
}
2. 实体User类
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class User {
private int id;
private String name;
}
3. Metaspace类
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import static 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(V1_1, ACC_PUBLIC, "Class" + i, null,
"java/lang/Object", null);
// 定义构造函数<init>方法
MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>",
"()V", null, null);
// 第一个指令为加载this
mw.visitVarInsn(ALOAD, 0);
// 第二个指令为调用父类Object的构造函数
mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object",
"<init>", "()V", false);
// 第三条指令为return
mw.visitInsn(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);
}
}
}
注:记得VM参数
heap:-Xms16m -Xmx16m
nonheap:-XX:MetaspaceSize=16M -XX:MaxMetaspaceSize=16M
二、导出内存映像文件
1. 内存溢出自动导出
-XX:+HeapDumpOnOutOfMemoryError //当发生内存溢出的时候导出
-XX:HeapDumpPath=./ //导出目录
2. 使用jmap命令手动导出
[root@localhost ~]# jps -l 或者 ps -ef|grep java
13220 spring-boot-test-0.0.1-SNAPSHOT.jar
54391 sun.tools.jps.Jps
[root@localhost ~]# jmap -dump:format=b,file=heap2019.hprof 13220
Dumping heap to /root/heap2019.hprof ...
Heap dump file created
三、MAT分析内存溢出
1. 工具下载
下载地址:https://www.eclipse.org/mat/downloads.php
//我使用的是MemoryAnalyzer-1.9.0版本。MAT不同版本按键位置或功能可能会有不同。
2. 将堆信息导入到mat中分析
File --> Open Heap Dump.. --> Leak Suspects Report --> Finish
3. 生成分析报告
jstack实战死循环与死锁
打印Java进程,核心文件或远程调试服务器的Java线程堆栈跟踪。此命令是实验性的,不受支持。
官网地址:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstack.html#BABGJDIF
死锁问题
1. DEMO
@RestController
public class JstackDemo {
private Object lock1 = new Object();
private Object lock2 = new Object();
/**
* 死锁
* PS:Thread1中想要获取的locak2已经被 Thread2占有了
* Thread2中想要获取的locak1已经被 Thread1占有了
*/
@GetMapping("/deadlock")
public String read() {
new Thread(() -> {
synchronized (lock1) {
try {Thread.sleep(1000);} catch (Exception e) {}
synchronized (lock2){
System.out.println("Thread1 over");
}
}
}).start();
new Thread(() -> {
synchronized (lock2) {
try {Thread.sleep(1000);} catch (Exception e) {}
synchronized (lock1){
System.out.println("Thread2 over");
}
}
}).start();
return "deadlock";
}
}
2. 问题排查
nohup java -jar xx.jar &
//top:找到占用CPU最大的进程号
top -p pid -H
//控制台输出线程的dump信息
jstack pid
printf "%s" //转化成十进制
printf "%x" //转化成十六进制
//或者
jstack pid > xxx.txt
sz xxx.txt
//死锁结果如下 :
Found one Java-level deadlock:
=============================
"Thread-103":
waiting to lock monitor 0x00007fe9d00062c8 (object 0x00000000e1ef4bc0, a java.lang.Object),
which is held by "Thread-5"
"Thread-5":
waiting to lock monitor 0x00007fe9dc029a58 (object 0x00000000e1ef4bb0, a java.lang.Object),
which is held by "Thread-4"
"Thread-4":
waiting to lock monitor 0x00007fe9d00062c8 (object 0x00000000e1ef4bc0, a java.lang.Object),
which is held by "Thread-5"
Java stack information for the threads listed above:
===================================================
"Thread-103":
at com.ceair.JstackDemo.lambda$read$1(JstackDemo.java:30)
- waiting to lock <0x00000000e1ef4bc0> (a java.lang.Object)
at com.ceair.JstackDemo$$Lambda$390/489447259.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"Thread-5":
at com.ceair.JstackDemo.lambda$read$1(JstackDemo.java:32)
- waiting to lock <0x00000000e1ef4bb0> (a java.lang.Object)
- locked <0x00000000e1ef4bc0> (a java.lang.Object)
at com.ceair.JstackDemo$$Lambda$390/489447259.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"Thread-4":
at com.ceair.JstackDemo.lambda$read$0(JstackDemo.java:24)
- waiting to lock <0x00000000e1ef4bc0> (a java.lang.Object)
- locked <0x00000000e1ef4bb0> (a java.lang.Object)
at com.ceair.JstackDemo$$Lambda$389/559309503.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
Found 1 deadlock.
死循环CPU飙高
1. DEMO
/**
* 死循环
* */
@GetMapping("/loop")
public List<Long> loop(){
String data = "{\"data\":[{\"partnerid\":]";
return getPartneridsFromJson(data);
}
public static List<Long> getPartneridsFromJson(String data){
//{\"data\":[{\"partnerid\":982,\"count\":\"10000\",\"cityid\":\"11\"},{\"partnerid\":983,\"count\":\"10000\",\"cityid\":\"11\"},{\"partnerid\":984,\"count\":\"10000\",\"cityid\":\"11\"}]}
//上面是正常的数据
List<Long> list = new ArrayList<Long>(2);
if(data == null || data.length() <= 0){
return list;
}
int datapos = data.indexOf("data");
if(datapos < 0){
return list;
}
int leftBracket = data.indexOf("[",datapos);
int rightBracket= data.indexOf("]",datapos);
if(leftBracket < 0 || rightBracket < 0){
return list;
}
String partners = data.substring(leftBracket+1,rightBracket);
if(partners == null || partners.length() <= 0){
return list;
}
while(partners!=null && partners.length() > 0){
int idpos = partners.indexOf("partnerid");
if(idpos < 0){
break;
}
int colonpos = partners.indexOf(":",idpos);
int commapos = partners.indexOf(",",idpos);
if(colonpos < 0 || commapos < 0){
//partners = partners.substring(idpos+"partnerid".length());//1
continue;
}
String pid = partners.substring(colonpos+1,commapos);
if(pid == null || pid.length() <= 0){
//partners = partners.substring(idpos+"partnerid".length());//2
continue;
}
try{
list.add(Long.parseLong(pid));
}catch(Exception e){
//do nothing
}
partners = partners.substring(commapos);
}
return list;
}
2. 问题排查
//启动项目
nohup java -jar xx.jar &
//找到占用CPU最大的进程号
top
//查看具体进程线程信息
top -p pid -H
47174 root 20 0 3029452 241332 4836 R 32.3 12.0 0:29.69 java
47175 root 20 0 3029452 241332 4836 R 32.3 12.0 0:23.61 java
47179 root 20 0 3029452 241332 4836 R 32.0 12.0 0:09.66 java
47178 root 20 0 3029452 241332 4836 R 25.3 12.0 0:10.71 java
47180 root 20 0 3029452 241332 4836 R 25.3 12.0 0:08.56 java
47173 root 20 0 3029452 241332 4836 R 25.0 12.0 0:29.51 java
47177 root 20 0 3029452 241332 4836 R 25.0 12.0 0:10.14 java
//转化成十六进制
printf "%x" 47174
//printf "%s" //转化成十进制
//printf "%x" //转化成十六进制
//控制台输出线程的dump信息
jstack pid
//或者下载到本地
jstack pid > xxx.txt
sz xxx.txt
"http-nio-8088-exec-2" #16 daemon prio=5 os_prio=0 tid=0x00007fa2e51af000 nid=0xb846 runnable [0x00007fa2b49ec000]
java.lang.Thread.State: RUNNABLE
at java.lang.String.indexOf(String.java:1769)
at java.lang.String.indexOf(String.java:1718)
at com.ceair.JstackDemo.getPartneridsFromJson(JstackDemo.java:77)
at com.ceair.JstackDemo.loop(JstackDemo.java:48)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1039)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:897)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:109)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:109)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:109)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:109)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:853)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1587)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
- locked <0x00000000e1cea108> (a org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)