jvm監控與調優之jdk命令行工具

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) 的時間用于垃圾收集
      
-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. 生成分析報告

具體使用參考:http://www.lxweimin.com/p/54c8e11750e4

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)
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容