JVM13-调优1:JVM调优常用命令,jinfo-实时查看和调整JVM配置参数,jstat-查看虚拟机性能统计信息,jstack-查看线程堆栈信息,jmap-生成堆转储快照
- jps-查看Java进程
- jinfo-实时查看和调整JVM配置参数
- jstat-查看虚拟机性能统计信息
- 用法
- 实战
- 1.-class 输出类加载情况的统计,统计JVM中加载的类的数量与size
- 2.-compiler 输出HotSpot中即时编译器编译情况的统计
- 3.-gc 输出JVM中堆的垃圾收集情况的统计
- 4.-gccapacity 输出新生代、老生代及持久代的存储容量情况
- 5.-gcmetacapacity 输出metaspace的大小
- 6.-gcnew 输出新生代垃圾收集的情况
- 7.-gcnewcapacity 输出新生代存储容量的情况
- 8.-gcold 输出老生代及持久代垃圾收集的情况
- 9.-gcoldcapacity 输出老生代的容量
- 10.-gcutil 输出垃圾收集信息
- 11.-gccause 输出垃圾收集信息(同-gcutil),同时显示最后一次仅当前正在发生的垃圾收集的原因
- 12.-printcompilation 输出JIT编译的方法信息
- 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日志的时候再说。