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)