Skip to content

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
统计指标数值单位/描述
Loaded12769已加载类的数量
Bytes25027.5已加载类占用的总字节数
Unloaded0卸载类的数量
Bytes0.0卸载类所释放的总字节数
Time3.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
参数含义
S0C2560.0Survivor 0区(Survivor Space 0)当前容量,单位为KB
S1C2560.0Survivor 1区(Survivor Space 1)当前容量,单位为KB
S0U0.0Survivor 0区当前使用量,单位为KB
S1U1805.1Survivor 1区当前使用量,单位为KB
EC20608.0Eden区当前容量,单位为KB
EU17451.4Eden区当前使用量,单位为KB
OC51412.0Old代(老年代)当前容量,单位为KB
OU42849.6Old代(老年代)当前使用量,单位为KB
MC75328.0方法区或Metaspace当前容量,单位为KB
MU74773.2方法区或Metaspace当前使用量,单位为KB
CCSC8384.0Compressed Class Space(压缩类空间)当前容量,单位为KB
CCSU8102.1Compressed Class Space(压缩类空间)当前使用量,单位为KB
YGC59年轻代垃圾回收次数
YGCT0.259年轻代垃圾回收累计耗时,单位为秒
FGC3Full GC(全局垃圾回收)次数
FGCT0.140Full GC累计耗时,单位为秒
CGC-并发标记周期数(CMS并发收集器相关),从输出看该值未被记录或不适用
CGCT-并发标记阶段总耗时,单位为秒,同样未显示具体数值
GCT0.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的内存结构

image-20240211000305452

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
统计指标数值含义
Compiled12805已成功编译的方法总数
Failed1编译失败的方法数量
Invalid0失效的已编译方法数量(例如类被卸载或方法删除)
Time36.75自JVM启动以来,用于编译方法所消耗的总时间(秒)
FailedType1最近一次编译失败的方法所属类型编号
FailedMethodjava/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
统计指标数值方法详情
Compiled12805已成功编译的方法总数
Size1412最近一次被编译方法的字节码大小(字节)
Type1编译类型标识,具体含义取决于JVM实现
Methodorg/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

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函数

image-20240211083320975

Histogram

image-20240211083706747

右击cn.diyai.model.MPAccessCode -> Merge Shortest Paths to GC Roots -> exclude all phantom/weak/soft etc.references

查看GC Roots

image-20240211094410726

查看对象数量

image-20240211095028934

查看对象所占的内存

jstack

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秒)
线程IDtid=0x00007fca6005e0c0
Native线程IDnid=0x346e
内存地址[0x00007fca67721000]
线程状态java.lang.Thread.State: RUNNABLE当前线程状态

Java线程状态

Thread StateDescription
NEWThe thread has not yet started.
RUNNABLEThe thread is executing in the JVM.
BLOCKEDThe thread is blocked waiting for a monitor lock.
WAITINGThe thread is waiting indefinitely for another thread to perform a particular action.
TIMED_WAITINGThe thread is waiting for another thread to perform an action for up to a specified waiting time.
TERMINATEDThe 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.

可发现有死锁问题