JVM13-调优1:JVM调优常用命令,jinfo-实时查看和调整JVM配置参数,jstat-查看虚拟机性能统计信息,jstack-查看线程堆栈信息,jmap-生成堆转储快照

JVM13-调优1:JVM调优常用命令,jinfo-实时查看和调整JVM配置参数,jstat-查看虚拟机性能统计信息,jstack-查看线程堆栈信息,jmap-生成堆转储快照

jps-查看Java进程

jps用于查看Java进程。

The jps command lists the instrumented Java HotSpot VMs on the target system. The command is limited to reporting information on JVMs for which it has the access permissions.

jps命令列出目标系统上已插装的Java HotSpot虚拟机。该命令仅限于报告jvm的信息,访问权限。

启动一个测试的Java进程,让它一直执行,使用jps命令来查看它

public class Test {

    public static void main(String[] args) throws InterruptedException {
        while (true){
            System.out.println(System.currentTimeMillis());
            Thread.sleep(3*1000);
        }
    }
}

在这里插入图片描述

jinfo-实时查看和调整JVM配置参数

jinfo用于实时查看和调整JVM配置参数。

The jinfo command prints Java configuration information for a specified Java process or core file or a remote debug server. The configuration information includes Java system properties and Java Virtual Machine (JVM) command-line flags.

jinfo命令打印指定Java的Java配置信息进程或核心文件或远程调试服务器。配置信息包括Java系统属性和Java虚拟机(JVM)命令行flag。

用法

C:\Users\郝开>jinfo -help
Usage:
    jinfo [option] <pid>
        (to connect to running process)
    jinfo [option] <executable <core>
        (to connect to a core file)
    jinfo [option] [server_id@]<remote server IP or hostname>
        (to connect to remote debug server)

where <option> is one of:
其中<option>是以下选项之一:
    -flag <name>         to print the value of the named VM flag		查看某个Java进程的name属性的值,这里注意name是区分大小写的
    -flag [+|-]<name>    to enable or disable the named VM flag			修改某个Java进程的name属性的值(参数只有被标记为manageable的flags可以被实时修改)
    -flag <name>=<value> to set the named VM flag to the given value	修改某个Java进程的name属性的值(参数只有被标记为manageable的flags可以被实时修改)
    -flags               to print VM flags								查看某个Java进程的曾经赋过值的一些参数
    -sysprops            to print Java system properties				查看某个Java进程的Java系统属性
    <no option>          to print both of the above						输出以上所有的
    -h | -help           to print this help message						输出帮助消息

实战

使用jps查看Java进程,然后根据输出的对应pid执行操作

1.查看某个Java进程的name属性的值

jinfo -flag UseG1GC PID

布尔类型的属性,+表示启用,-表示没启用
在这里插入图片描述

jinfo -flag MaxHeapSize PID

key-value的属性,输出对应值
在这里插入图片描述

2.查看某个Java进程的曾经赋过值的一些参数

jinfo -flags PID

在这里插入图片描述

jstat-查看虚拟机性能统计信息

jstat用于查看虚拟机性能统计信息。

The jstat command displays performance statistics for an instrumented Java HotSpot VM. The target JVM is identified by its virtual machine identifier, or vmid option.

jstat命令显示插装Java的性能统计信息HotSpot VM。目标JVM由其虚拟机标识符或标识符标识的vmid选项。

用法

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.

Definitions(定义)
option:参数选项,可以使用jstat -options命令查看对应的options选项
-t:可以在打印的列加上Timestamp列,用于显示系统运行的时间
-h:可以在周期性数据输出的时候,指定输出多少行以后输出一次表头
vmid:Virtual Machine ID( 进程的 pid)
interval:执行每次的间隔时间,单位为毫秒
count:用于指定输出多少次记录,缺省则会一直打印
option 可以从下面参数中选择

查看对应的options选项
在这里插入图片描述

C:\Users\郝开>jstat -options
-class					输出类加载情况的统计,统计JVM中加载的类的数量与size
-compiler				输出HotSpot中即时编译器编译情况的统计
-gc						输出JVM中堆的垃圾收集情况的统计
-gccapacity				输出新生代、老生代及持久代的存储容量情况
-gcmetacapacity			输出metaspace的大小
-gcnew					输出新生代垃圾收集的情况
-gcnewcapacity			输出新生代存储容量的情况
-gcold					输出老生代及持久代垃圾收集的情况
-gcoldcapacity			输出老生代的容量
-gcutil					输出垃圾收集信息
-gccause				输出垃圾收集信息(同-gcutil),同时显示最后一次仅当前正在发生的垃圾收集的原因
-printcompilation		输出JIT编译的方法信息

实战

1.-class 输出类加载情况的统计,统计JVM中加载的类的数量与size

C:\Users\郝开>jps
22384 Test
23152 Launcher
4192 Jps
6332

C:\Users\郝开>jstat -class 22384
Loaded  Bytes  Unloaded  Bytes     Time
   635  1274.3        0     0.0       0.06

Loaded:加载类的数量
Bytes:加载类的size,单位为Byte
Unloaded:卸载类的数目
Bytes:卸载类的size,单位为Byte
Time:加载或者卸载类花费的时间

输出某个java进程的类装载信息,每1000毫秒输出一次,共输出5次,其它命令同理

C:\Users\郝开>jstat -class 22384 1000 5
Loaded  Bytes  Unloaded  Bytes     Time
   635  1274.3        0     0.0       0.06
   635  1274.3        0     0.0       0.06
   635  1274.3        0     0.0       0.06
   635  1274.3        0     0.0       0.06
   635  1274.3        0     0.0       0.06

2.-compiler 输出HotSpot中即时编译器编译情况的统计

C:\Users\郝开>jstat -compiler 22384
Compiled Failed Invalid   Time   FailedType FailedMethod
     142      0       0     0.09          0

Compiled:编译任务执行数量
Failed:编译任务执行失败数量
Invalid:编译任务执行失效数量
Time:编译任务消耗时间
FailedType:最后一个编译失败任务的类型
FailedMethod:最后一个编译失败任务所在的类及方法

3.-gc 输出JVM中堆的垃圾收集情况的统计

C:\Users\郝开>jstat -gc 22384
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
10752.0 10752.0  0.0    0.0   64512.0   9032.0   172032.0     0.0     4480.0 776.5  384.0   76.6       0    0.000   0      0.000    0.000

S0C:年轻代S0的容量(字节)
S1C:年轻代S1的容量(字节)
S0U:年轻代S0目前已使用空间(字节)
S1U:年轻代S1目前已使用空间(字节)
EC:年轻代Eden的容量(字节)
EU:年轻代Eden目前已使用空间(字节)
OC:Old代的容量(字节)
OU:Old代目前已使用空间(字节)
MC:metaspace(元空间)的容量(字节)
MU:metaspace(元空间)目前已使用空间(字节)
CCSC:当前压缩类空间的容量(字节)
CCSU:当前压缩类空间目前已使用空间(字节)
YGC:Young GC次数
YGCT:从应用程序启动到采样时Young GC所用时间(s)
FGC:从应用程序启动到采样时Full GC次数
FGCT:从应用程序启动到采样时Full GC所用时间(s)
GCT:从应用程序启动到采样时GC用的总时间(s)

4.-gccapacity 输出新生代、老生代及持久代的存储容量情况

C:\Users\郝开>jstat -gccapacity 22384
 NGCMN    NGCMX     NGC     S0C   S1C       EC      OGCMN      OGCMX       OGC         OC       MCMN     MCMX      MC     CCSMN    CCSMX     CCSC    YGC    FGC
 86016.0 1374720.0  86016.0 10752.0 10752.0  64512.0   172032.0  2749952.0   172032.0   172032.0      0.0 1056768.0   4480.0      0.0 1048576.0    384.0      0     0

NGCMN:年轻代中初始化(最小)的大小(字节)
NGCMX:年轻代的最大容量(字节)
NGC:年轻代中当前的容量(字节)
S0C:年轻代S0的容量(字节)
S1C:年轻代S1的容量(字节)
EC:年轻代中Eden的容量(字节)
OGCMN:Old代中初始化(最小)的大小(字节)
OGCMX:Old代的最大容量(字节)
OGC:Old代当前新生成的容量(字节)
OC:Old代的容量(字节)
MCMN:metaspace(元空间)中初始化(最小)的大小 (字节)
MCMX:metaspace(元空间)的最大容量 (字节)
MC:metaspace(元空间)当前新生成的容量 (字节)
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间的容量(字节)
YGC:从应用程序启动到采样时Young GC次数
FGC:从应用程序启动到采样时Full GC次数

5.-gcmetacapacity 输出metaspace的大小

C:\Users\郝开>jstat -gcmetacapacity 22384
   MCMN       MCMX        MC       CCSMN      CCSMX       CCSC     YGC   FGC    FGCT     GCT
       0.0  1056768.0     4480.0        0.0  1048576.0      384.0     0     0    0.000    0.000

MCMN:最小元数据容量
MCMX:最大元数据容量
MC:metaspace(元空间)的容量 (字节)
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间的容量(字节)
YGC:从应用程序启动到采样时Young GC次数
FGC:从应用程序启动到采样时Full GC次数
FGCT:从应用程序启动到采样时Full GC所用时间(s)
GCT:从应用程序启动到采样时GC用的总时间(s)

6.-gcnew 输出新生代垃圾收集的情况

C:\Users\郝开>jstat -gcnew 22384
 S0C    S1C    S0U    S1U   TT MTT  DSS      EC       EU     YGC     YGCT
10752.0 10752.0    0.0    0.0 15  15    0.0  64512.0   9032.0      0    0.000

S0C:年轻代S0的容量(字节)
S1C:年轻代S1的容量(字节)
S0U:年轻代S0目前已使用空间(字节)
S1U:年轻代S1目前已使用空间(字节)
TT:持有次数限制
MTT:最大持有次数限制
DSS:期望的幸存区大小
EC:年轻代中Eden(伊甸园)的容量 (字节)
EU:年轻代中Eden(伊甸园)目前已使用空间 (字节)
YGC:从应用程序启动到采样时年轻代中gc次数
YGCT:从应用程序启动到采样时年轻代中gc所用时间(s)

7.-gcnewcapacity 输出新生代存储容量的情况

C:\Users\郝开>jstat -gcnewcapacity 22384
  NGCMN      NGCMX       NGC      S0CMX     S0C     S1CMX     S1C       ECMX        EC      YGC   FGC
   86016.0  1374720.0    86016.0 458240.0  10752.0 458240.0  10752.0  1373696.0    64512.0     0     0

NGCMN:年轻代中初始化(最小)的大小(字节)
NGCMX:年轻代的最大容量(字节)
NGC:年轻代中当前的容量(字节)
S0CMX:年轻代中S0的最大容量(字节)
S0C:年轻代中S0的容量(字节)
S1CMX:年轻代中S1的最大容量(字节)
S1C:年轻代中S1的容量(字节)
ECMX:年轻代中Eden的最大容量(字节)
EC:年轻代中Eden的容量(字节)
YGC:从应用程序启动到采样时Young GC次数
FGC:从应用程序启动到采样时Full GC次数

8.-gcold 输出老生代及持久代垃圾收集的情况

C:\Users\郝开>jstat -gcold 22384
   MC       MU      CCSC     CCSU       OC          OU       YGC    FGC    FGCT     GCT
  4480.0    776.5    384.0     76.6    172032.0         0.0      0     0    0.000    0.000

MC:metaspace(元空间)的容量 (字节)
MU:metaspace(元空间)目前已使用空间 (字节)
CCSC:当前压缩类空间的容量(字节)
CCSU:当前压缩类空间目前已使用空间(字节)
OC:Old代的容量 (字节)
OU:Old代目前已使用空间 (字节)
YGC:从应用程序启动到采样时Young GC次数
FGC:从应用程序启动到采样时Full GC次数
FGCT:从应用程序启动到采样时Full GC所用时间(s)
GCT:从应用程序启动到采样时GC用的总时间(s)

9.-gcoldcapacity 输出老生代的容量

C:\Users\郝开>jstat -gcoldcapacity 22384
   OGCMN       OGCMX        OGC         OC       YGC   FGC    FGCT     GCT
   172032.0   2749952.0    172032.0    172032.0     0     0    0.000    0.000

OGCMN:Old代中初始化(最小)的大小(字节)
OGCMX:Old代的最大容量(字节)
OGC:Old代当前新生成的容量(字节)
OC:Old代的容量 (字节)
YGC:从应用程序启动到采样时Young GC次数
FGC:从应用程序启动到采样时Full GC次数
FGCT:从应用程序启动到采样时Full GC所用时间(s)
GCT:从应用程序启动到采样时GC用的总时间(s)

10.-gcutil 输出垃圾收集信息

C:\Users\郝开>jstat -gcutil 22384
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0.00   0.00  14.00   0.00  17.33  19.94      0    0.000     0    0.000    0.000

S0:年轻代中S0已使用的占当前容量百分比
S1:年轻代中S1已使用的占当前容量百分比
E:年轻代中Eden(伊甸园)已使用的占当前容量百分比
O:old代已使用的占当前容量百分比
M:元数据区已使用的占当前容量百分比
CCS:压缩类空间已使用的占当前容量百分比
YGC:从应用程序启动到采样时Young GC次数
YGCT :从应用程序启动到采样时Young GC所用时间(s)
FGC:从应用程序启动到采样时Full GC次数
FGCT:从应用程序启动到采样时Full GC所用时间(s)
GCT:从应用程序启动到采样时gc用的总时间(s)

11.-gccause 输出垃圾收集信息(同-gcutil),同时显示最后一次仅当前正在发生的垃圾收集的原因

C:\Users\郝开>jstat -gccause 22384
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT    LGCC                 GCC
  0.00   0.00  14.00   0.00  17.33  19.94      0    0.000     0    0.000    0.000 No GC                No GC

LGCC:最后一次GC原因
GCC:当前GC原因(No GC 为当前没有执行GC)

12.-printcompilation 输出JIT编译的方法信息

C:\Users\郝开>jstat -printcompilation 22384
Compiled  Size  Type Method
     142     63    1 java/lang/Long toString

Compiled:编译任务的数目
Size:方法生成的字节码的大小
Type:编译类型
Method:类名和方法名用来标识编译的方法。类名使用/做为一个命名空间分隔符。方法名是给定类中的方法。上述格式是由-XX:+PrintComplation选项进行设置的

jstack-查看线程堆栈信息

The jstack command prints Java stack traces of Java threads for a specified Java process, core file, or remote debug server.

jstack命令打印指定Java的Java线程的Java堆栈跟踪进程、核心文件或远程调试服务器。

用法

C:\Users\郝开>jstack -help
Usage:
    jstack [-l] <pid>
        (to connect to running process)
    jstack -F [-m] [-l] <pid>
        (to connect to a hung process)
    jstack [-m] [-l] <executable> <core>
        (to connect to a core file)
    jstack [-m] [-l] [server_id@]<remote server IP or hostname>
        (to connect to a remote debug server)

Options:
    -F  to force a thread dump. Use when jstack <pid> does not respond (process is hung)	当jstack [-l] pid 没有响应时,强制打印栈信息
    -m  to print both java and native frames (mixed mode)									打印包含Java和本机C/C++帧的混合模式堆栈跟踪
    -l  long listing. Prints additional information about locks								长列表。打印关于锁的附加信息,例如属于java.util.concurrent的ownable synchronizers列表
    -h or -help to print this help message													输出帮助信息

实战:排查死锁案例

正常CPU40~50%就比较高了,可以检查是否有死锁阻塞了进程,导致还咋子一直消耗CPU的核心算力,导致CPU使用率升高。

// 运行主类
public class DeadLockDemo {
    public static void main(String[] args) {
        DeadLock d1 = new DeadLock(true);
        DeadLock d2 = new DeadLock(false);
        Thread t1 = new Thread(d1);
        t1.setName("t1");
        Thread t2 = new Thread(d2);
        t2.setName("t2");
        t1.start();
        t2.start();
    }
}

// 定义锁对象
class MyLock {
    public static Object obj1 = new Object();
    public static Object obj2 = new Object();
}

// 死锁代码
class DeadLock implements Runnable {
    private boolean flag;

    DeadLock(boolean flag) {
        this.flag = flag;
    }

    public void run() {
        if (flag) {
            while (true) {
                synchronized (MyLock.obj1) {
                    System.out.println(Thread.currentThread().getName() + "----if获得obj1锁");
                    synchronized (MyLock.obj2) {
                        System.out.println(Thread.currentThread().getName() + "---- if获得obj2锁");
                    }
                }
            }
        } else {
            while (true) {
                synchronized (MyLock.obj2) {
                    System.out.println(Thread.currentThread().getName() + "----else获得obj2锁");
                    synchronized (MyLock.obj1) {
                        System.out.println(Thread.currentThread().getName() + "---- else获得obj1锁");
                    }
                }
            }
        }
    }
}

在这里插入图片描述
jstack PID打印线程信息,把打印信息拉到最后可以发现
在这里插入图片描述
完整输出如下

C:\Users\郝开>jps
1092 DeadLockDemo
15220
20212 RemoteMavenServer36
7384 Jps
25772 Launcher

C:\Users\郝开>jstack 1092
2023-09-29 10:46:26
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.271-b09 mixed mode):

"DestroyJavaVM" #22 prio=5 os_prio=0 tid=0x0000026c199c2000 nid=0x5298 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"t2" #21 prio=5 os_prio=0 tid=0x0000026c38118800 nid=0x2e84 waiting for monitor entry [0x0000009a065ff000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.example.jvmdemo.jvmshizhan.DeadLock.run(DeadLockDemo.java:46)
        - waiting to lock <0x000000076c911f50> (a java.lang.Object)
        - locked <0x000000076c911f60> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:748)

"t1" #20 prio=5 os_prio=0 tid=0x0000026c38118000 nid=0x27e4 waiting for monitor entry [0x0000009a064fe000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.example.jvmdemo.jvmshizhan.DeadLock.run(DeadLockDemo.java:37)
        - waiting to lock <0x000000076c911f60> (a java.lang.Object)
        - locked <0x000000076c911f50> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:748)

"Service Thread" #19 daemon prio=9 os_prio=0 tid=0x0000026c37eb8800 nid=0x3024 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread11" #18 daemon prio=9 os_prio=2 tid=0x0000026c36b07000 nid=0x5e44 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread10" #17 daemon prio=9 os_prio=2 tid=0x0000026c36b05800 nid=0x36d8 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread9" #16 daemon prio=9 os_prio=2 tid=0x0000026c36b02000 nid=0x4154 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread8" #15 daemon prio=9 os_prio=2 tid=0x0000026c36b08800 nid=0x2850 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread7" #14 daemon prio=9 os_prio=2 tid=0x0000026c36af9000 nid=0x4008 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread6" #13 daemon prio=9 os_prio=2 tid=0x0000026c36ae6000 nid=0x5c04 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread5" #12 daemon prio=9 os_prio=2 tid=0x0000026c36add000 nid=0x11ec waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread4" #11 daemon prio=9 os_prio=2 tid=0x0000026c36adb000 nid=0x673c waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread3" #10 daemon prio=9 os_prio=2 tid=0x0000026c36ad4800 nid=0xb2c waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x0000026c36ad2800 nid=0x64e0 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x0000026c36ad2000 nid=0x17e4 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x0000026c36ad0000 nid=0x11c8 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x0000026c36acc000 nid=0x3d68 runnable [0x0000009a055ff000]
   java.lang.Thread.State: RUNNABLE
        at java.net.SocketInputStream.socketRead0(Native Method)
        at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
        at java.net.SocketInputStream.read(SocketInputStream.java:171)
        at java.net.SocketInputStream.read(SocketInputStream.java:141)
        at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
        at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
        at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
        - locked <0x000000076c44fb30> (a java.io.InputStreamReader)
        at java.io.InputStreamReader.read(InputStreamReader.java:184)
        at java.io.BufferedReader.fill(BufferedReader.java:161)
        at java.io.BufferedReader.readLine(BufferedReader.java:324)
        - locked <0x000000076c44fb30> (a java.io.InputStreamReader)
        at java.io.BufferedReader.readLine(BufferedReader.java:389)
        at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:55)

"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x0000026c36884800 nid=0x4214 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x0000026c368f0000 nid=0x6714 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x0000026c346f4800 nid=0x2218 in Object.wait() [0x0000009a052fe000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000076c188ee0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
        - locked <0x000000076c188ee0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)

"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000026c36873000 nid=0x3754 in Object.wait() [0x0000009a051ff000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000076c186c00> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x000000076c186c00> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=2 tid=0x0000026c346db000 nid=0x4a0c runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000026c199d9800 nid=0x27a8 runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000026c199da800 nid=0x475c runnable

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000026c199dc000 nid=0x4870 runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000026c199dd800 nid=0x2c54 runnable

"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x0000026c199df800 nid=0x48a4 runnable

"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x0000026c199e0800 nid=0x5cc0 runnable

"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x0000026c199e3800 nid=0xb8c runnable

"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x0000026c199e4800 nid=0x3f7c runnable

"GC task thread#8 (ParallelGC)" os_prio=0 tid=0x0000026c199e5800 nid=0x5488 runnable

"GC task thread#9 (ParallelGC)" os_prio=0 tid=0x0000026c199e6800 nid=0x4910 runnable

"GC task thread#10 (ParallelGC)" os_prio=0 tid=0x0000026c199e9000 nid=0x3c68 runnable

"GC task thread#11 (ParallelGC)" os_prio=0 tid=0x0000026c199ec000 nid=0x77c runnable

"GC task thread#12 (ParallelGC)" os_prio=0 tid=0x0000026c199ed000 nid=0x5c40 runnable

"GC task thread#13 (ParallelGC)" os_prio=0 tid=0x0000026c199ee800 nid=0x5930 runnable

"GC task thread#14 (ParallelGC)" os_prio=0 tid=0x0000026c199ef800 nid=0x1ff8 runnable

"VM Periodic Task Thread" os_prio=2 tid=0x0000026c37f2a000 nid=0x44bc waiting on condition

JNI global references: 12


Found one Java-level deadlock:
=============================
"t2":
  waiting to lock monitor 0x0000026c346f19a8 (object 0x000000076c911f50, a java.lang.Object),
  which is held by "t1"
"t1":
  waiting to lock monitor 0x0000026c346f4188 (object 0x000000076c911f60, a java.lang.Object),
  which is held by "t2"

Java stack information for the threads listed above:
===================================================
"t2":
        at com.example.jvmdemo.jvmshizhan.DeadLock.run(DeadLockDemo.java:46)
        - waiting to lock <0x000000076c911f50> (a java.lang.Object)
        - locked <0x000000076c911f60> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:748)
"t1":
        at com.example.jvmdemo.jvmshizhan.DeadLock.run(DeadLockDemo.java:37)
        - waiting to lock <0x000000076c911f60> (a java.lang.Object)
        - locked <0x000000076c911f50> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.


C:\Users\郝开>

jmap-生成堆转储快照

The jmap command prints shared object memory maps or heap memory details of a specified process, core file, or remote debug server.

jmap命令打印共享对象内存映射或堆内存详细信息指定的进程、核心文件或远程调试服务器。

用法

Usage:
    jmap [option] <pid>
        (to connect to running process)
    jmap [option] <executable <core>
        (to connect to a core file)
    jmap [option] [server_id@]<remote server IP or hostname>
        (to connect to remote debug server)

where <option> is one of:
其中<option>是以下选项之一:
    <none>               to print same info as Solaris pmap						查看进程的内存映像信息
    -heap                to print java heap summary								输出Java堆详细信息:GC使用的算法,heap(堆)的配置及JVM堆内存的使用情况
    -histo[:live]        to print histogram of java object heap; if the "live"	输出每个class的实例数目、内存占用、类全名信息、VM的内部类名字开头会加上前缀“*”;如果live子参数加上后,只统计存活的对象数量。
                         suboption is specified, only count live objects
    -clstats             to print class loader statistics						输出类加载器信息
    -finalizerinfo       to print information on objects awaiting finalization	输出在F-Queue队列等待Finalizer线程执行finalizer方法的对象
    -dump:<dump-options> to dump java heap in hprof binary format
                         dump-options:
                           live         dump only live objects; if not specified,
                                        all objects in the heap are dumped.
                           format=b     binary format
                           file=<file>  dump heap to <file>
                         Example: jmap -dump:live,format=b,file=heap.bin <pid>
    -F                   force. Use with -dump:<dump-options> <pid> or -histo
                         to force a heap dump or histogram when <pid> does not
                         respond. The "live" suboption is not supported
                         in this mode.
    -h | -help           to print this help message
    -J<flag>             to pass <flag> directly to the runtime system

-F:当虚拟机对-dump命令没有反应时,可以使用这个选项强制生成dump快照,再在Linux/Solaris平台下有效

实战

打印出堆内存相关信息

jmap -heap PID可以打印出各个内存分区的信息

C:\Users\郝开>jps
15220
17656 Launcher
15740 Test
18028 Jps

C:\Users\郝开>jmap -heap 15740
Attaching to process ID 15740, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.271-b09

using thread-local object allocation.
Parallel GC with 15 thread(s)		

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 4223664128 (4028.0MB)
   NewSize                  = 88080384 (84.0MB)
   MaxNewSize               = 1407713280 (1342.5MB)
   OldSize                  = 176160768 (168.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 66060288 (63.0MB)
   used     = 9248736 (8.820281982421875MB)
   free     = 56811552 (54.179718017578125MB)
   14.000447591145834% used
From Space:
   capacity = 11010048 (10.5MB)
   used     = 0 (0.0MB)
   free     = 11010048 (10.5MB)
   0.0% used
To Space:
   capacity = 11010048 (10.5MB)
   used     = 0 (0.0MB)
   free     = 11010048 (10.5MB)
   0.0% used
PS Old Generation
   capacity = 176160768 (168.0MB)
   used     = 0 (0.0MB)
   free     = 176160768 (168.0MB)
   0.0% used

3198 interned Strings occupying 262088 bytes.
首先查看GC的垃圾收集器用的是哪个

因为G1的垃圾收集器和其他的不一样,它对堆内存进行了重新定义,划分成了一个个Region。

using thread-local object allocation.
Parallel GC with 15 thread(s)						这里输出用到的GC是Parallel GC,GC线程是15
Heap Configuration:堆配置信息
Heap Configuration:
   MinHeapFreeRatio         = 0								最小自由堆内存0%
   MaxHeapFreeRatio         = 100							最大自由堆内存100%
   MaxHeapSize              = 4223664128 (4028.0MB)			最大堆大小
   NewSize                  = 88080384 (84.0MB)				年轻代大小	
   MaxNewSize               = 1407713280 (1342.5MB)			年轻代最大大小
   OldSize                  = 176160768 (168.0MB)			老年代大小
   NewRatio                 = 2								老生代与新生代的比值(Old:Young)。比如-XX:NewRatio=4,则表示老年代:新生代=4:1,也就是新生代占整个堆内存的1/5
   SurvivorRatio            = 8								两个S区和Eden区的比值。比如-XX:SurvivorRatio=8,也就是(S0+S1):Eden=2:8,也就是一个S占整个新生代的1/10
   MetaspaceSize            = 21807104 (20.796875MB)		方法区(元空间)大小
   CompressedClassSpaceSize = 1073741824 (1024.0MB)			
   MaxMetaspaceSize         = 17592186044415 MB				方法区(元空间)最大大小
   G1HeapRegionSize         = 0 (0.0MB)						G1的Region的大小

可以看到NewSize+OldSize小于MaxHeapSize,那是因为这些已经够用了,因此不再从直接内存中继续抢占内存资源。

这里设置的新老生代的比值为1:2,NewSize:OldSize=84:168,有些时候,也可能是84:169。

因为计算过程时这样的:
先通过整个堆空间乘1/3计算得出NewSize,
Java根据NewSize计算OldSize:首先会直接去除NewSize小数点的尾部,而不是四舍五入,再用整个堆内存减去去除尾部的NewSize得出OldSize

假设堆空间是253,那么NewSize=253/3=84.333,OldSize=253-84=169。

Heap Usage:堆实际使用信息,以及各个区域的大小

选用的GC垃圾回收算法,这里对应的输出内容也会不同。

Heap Usage:
PS Young Generation							年轻代
Eden Space:										Eden区
   capacity = 66060288 (63.0MB)						容积,总空间大小
   used     = 9248736 (8.820281982421875MB)			使用空间	
   free     = 56811552 (54.179718017578125MB)		空闲空间	
   14.000447591145834% used							使用率,百分数
From Space:										From区,对应S区的其中一个
   capacity = 11010048 (10.5MB)						容积,总空间大小
   used     = 0 (0.0MB)								使用空间
   free     = 11010048 (10.5MB)						空闲空间
   0.0% used										使用率,百分数
To Space:										To区,对应S区的其中一个
   capacity = 11010048 (10.5MB)						容积,总空间大小
   used     = 0 (0.0MB)								使用空间
   free     = 11010048 (10.5MB)						空闲空间	
   0.0% used										使用率,百分数	
PS Old Generation							老年代
   capacity = 176160768 (168.0MB)				容积,总空间大小
   used     = 0 (0.0MB)							使用空间
   free     = 176160768 (168.0MB)				空闲空间
   0.0% used									使用率,百分数

jmap dump出heap信息文件

jmap -dump:format=b,file=heap.hprof PID

在OOM时自动dump出heap信息文件

线上这样去分析肯定有问题,肯定是希望如果发生堆内存溢出的情况,能自动的将这些信息转储成一个文件,以便于我们去分析它。一般在开发中,JVM参数可以加上下面两句,这样内存溢出时,会自动dump出该文件

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap.hprof

-XX:+HeapDumpOnOutOfMemoryError表示开启OOM异常,-XX:HeapDumpPath表示dump出的文件路径,这里没指定目录,就会dump到应用程序的根目录下面(Java项目中,根目录是指项目的最顶层目录,通常是项目创建的文件夹中的第一个目录)。

模拟一下堆内存溢出

新建一个spring boot项目,定义一个业务类

public class Worker {
    private Worker worker;
    private Integer id;
    private String username;
    private String password;

    public Worker getWorker() {
        return worker;
    }

    public void setWorker(Worker worker) {
        this.worker = worker;
    }

    public Integer getId() {
        return id;
    }

    public String getPassword() {
        return password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return super.toString();
    }
}

定义一个Controller

import com.example.jvmdemo.pojo.Worker;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@RestController
public class HeapController {
    // new ArrayList<>,使用new,即堆中为这个实例开辟内存空间,并将这个实例赋值给引用变量list
    List<Worker> list = new ArrayList<>();

    @GetMapping("/heap")
    public String heap() {
        // 无限的向引用变量里面添加对象
        while (true) {
            list.add(new Worker());
        }
    }
}

启动参数设置堆内存大小: -Xms20M -Xmx20M
在这里插入图片描述
启动,然后访问localhost:8080/heap,使得堆内存溢出
在这里插入图片描述
没指定目录,就会dump到应用程序的根目录下面在这里插入图片描述

关于dump下来的文件

一般dump下来的文件可以结合工具来分析,是查看GC日志的一个东西,这块GC日志的时候再说。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值