jmap 命令
1)查看历史生成的实例 jmap -histo pid
一般可以将结果输出查看,会很方便
$ jmap -histo 16677 > ./log.txt
打开log.txt文件显示如下:
- num:序号
- instances:实例数量
- bytes:占用空间大小
- class name:类名称,[C is a char[],[S is a short[],[I is a int[],[B is a byte[],[[I is a int[][]
2)查看当前存活的实例 jmap -histo:live pid
执行过程中可能会触发一次full gc
3)堆信息 jmap -heap
$ jmap -heap 3334
Attaching to process ID 3334, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.131-b11
using thread-local object allocation.
Parallel GC with 4 thread(s)
Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 2147483648 (2048.0MB)
NewSize = 44564480 (42.5MB)
MaxNewSize = 715653120 (682.5MB)
OldSize = 89653248 (85.5MB)
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 = 141557760 (135.0MB)
used = 126737968 (120.86674499511719MB)
free = 14819792 (14.133255004882812MB)
89.53092221860533% used
From Space:
capacity = 12058624 (11.5MB)
used = 0 (0.0MB)
free = 12058624 (11.5MB)
0.0% used
To Space:
capacity = 13107200 (12.5MB)
used = 0 (0.0MB)
free = 13107200 (12.5MB)
0.0% used
PS Old Generation
capacity = 101187584 (96.5MB)
used = 16652536 (15.881095886230469MB)
free = 84535048 (80.61890411376953MB)
16.457094182622246% used
19181 interned Strings occupying 1940984 bytes.
Heap Configuration 堆的配置信息
Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize 堆的最大容量 = 2147483648 (2048.0MB)
NewSize 新生代空间 = 44564480 (42.5MB)
MaxNewSize 新生代最大空间 = 715653120 (682.5MB)
OldSize 老年代空间 = 89653248 (85.5MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize 元空间 = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize 元空间最大容量 = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
Heap Usage 使用情况
Heap Usage:
PS Young Generation
Eden Space:
capacity = 141557760 (135.0MB)
used = 126737968 (120.86674499511719MB)
free = 14819792 (14.133255004882812MB)
89.53092221860533% used
From Space:
capacity = 12058624 (11.5MB)
used = 0 (0.0MB)
free = 12058624 (11.5MB)
0.0% used
To Space:
capacity = 13107200 (12.5MB)
used = 0 (0.0MB)
free = 13107200 (12.5MB)
0.0% used
PS Old Generation
capacity = 101187584 (96.5MB)
used = 16652536 (15.881095886230469MB)
free = 84535048 (80.61890411376953MB)
16.457094182622246% used
4) 堆内存dump
jmap -dump:format=b,file=文件名 pid
$ jmap -dump:format=b,file=springboot.hprof 3334
Dumping heap to /Users/huoyajing/springboot.hprof ...
Heap dump file created
设置内存溢出自动导出
也可以设置内存溢出自动导出dump文件(内存很大的时候,可能会导不出来)
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=./ (路径)
实例代码:
-Xms10M -Xmx10M -XX:+PrintGCDetails
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=
/Users/huoyajing/information/springCloud/jvm/jvm1.dump
public class OOMTest {
public static void main(String[] args) {
// -Xms10M -Xmx10M -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/Users/huoyajing/information/springCloud/jvm/jvm1.dump
List<Object> list = new ArrayList<>();
long i = 0;
long j = 0;
while (true) {
list.add(new User(i++, UUID.randomUUID().toString(), "1", 1, "0"));
new User(j--, UUID.randomUUID().toString(), "1", 1, "0");
}
}
}
public class User {
private Long id;
private String name;
private String password;
private Integer type;
private String status;
public User(){
}
public User(Long id, String name, String password, int type, String status) {
super();
this.id=id;
this.name=name;
this.password=password;
this.type=type;
this.status=status;
}
执行结果:
java.lang.OutOfMemoryError: Java heap space
Dumping heap to /Users/huoyajing/information/springCloud/jvm/jvm1.dump ...
Heap dump file created [13278771 bytes in 0.102 secs]
Heap
PSYoungGen total 2560K, used 2048K [0x00000007bfd00000, 0x00000007c0000000, 0x00000007c0000000)
eden space 2048K, 100% used [0x00000007bfd00000,0x00000007bff00000,0x00000007bff00000)
from space 512K, 0% used [0x00000007bff80000,0x00000007bff80000,0x00000007c0000000)
to space 512K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007bff80000)
ParOldGen total 7168K, used 7132K [0x00000007bf600000, 0x00000007bfd00000, 0x00000007bfd00000)
object space 7168K, 99% used [0x00000007bf600000,0x00000007bfcf72d8,0x00000007bfd00000)
Metaspace used 4235K, capacity 4668K, committed 4864K, reserved 1056768K
class space used 476K, capacity 492K, committed 512K, reserved 1048576K
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:261)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
at java.util.ArrayList.add(ArrayList.java:458)
at com.huohuo.service.jvm.OOMTest.main(OOMTest.java:22)
jstat 命令
jstat命令可以查看堆内存各部分的使用量,以及加载类的数量。命令的格式如下:
jstat [-命令选项] [vmid] [间隔时间(毫秒)] [查询次数]
注意:使用的jdk版本是jdk8
1)垃圾回收统计 jstat -gc pid (最常用)
$ jstat -gc 16677
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT CGC CGCT GCT
11776.0 3072.0 0.0 2720.0 41984.0 17226.5 104960.0 49804.8 56488.0 52498.3 9128.0 7787.2 147 2.308 9 1.730 - - 4.038
S0C:第一个幸存区的大小,单位KB
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:eden的大小
EU:eden的使用大小
OC:old区大小
OU:old区使用大小
MC:元空间大小(元空间)
MU:元空间使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
注意:是从项目启动开始算起(而非本次命令)
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间,单位s
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间,单位s
GCT:垃圾回收消耗总时间,单位s
可以执行命令 jstat -gc pid 1000 10 (每隔1秒执行1次命令,共执行10次),通过观察EU(eden区的使用)来估算每秒eden大概新增多少对象,如果系统负载不高,可以把频率1秒换成1分钟,甚至10分钟来观察整体情况。注意,一般系统可能有高峰期和日常期,所以需要在不同的时间分别估算不同情况下对象增长速率。
2)堆内存统计 jstat -gccapacity pid
jstat -gccapacity 33133
NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC
43520.0 698880.0 184832.0 11776.0 13312.0 155648.0 87552.0 1398272.0 93184.0 93184.0 0.0 1081344.0 35416.0 0.0 1048576.0 4608.0 8 2
NGCMN:新生代最小容量
NGCMX:新生代最大容量
NGC:当前新生代容量
S0C:第一个幸存区大小
S1C:第二个幸存区的大小
EC:伊甸园区的大小
OGCMN:老年代最小容量
OGCMX:老年代最大容量
OGC:当前老年代大小
OC:当前老年代大小
MCMN:最小元数据容量
MCMX:最大元数据容量
MC:当前元数据空间大小
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:年轻代gc次数
FGC:老年代GC次数
3)新生代垃圾回收统计 jstat -gcnew 33133
jstat -gcnew 33133
S0C S1C S0U S1U TT MTT DSS EC EU YGC YGCT
11776.0 13312.0 0.0 0.0 3 15 13312.0 155648.0 126169.2 8 0.087
S0C:第一个幸存区的大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
TT:对象在新生代存活的次数
MTT:对象在新生代存活的最大次数
DSS:期望的幸存区大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
4)新生代内存统计 jstat -gcnewcapacity 33133
$ jstat -gcnewcapacity 33133
NGCMN NGCMX NGC S0CMX S0C S1CMX S1C ECMX EC YGC FGC
43520.0 698880.0 184832.0 232960.0 11776.0 232960.0 13312.0 697856.0 155648.0 8 2
NGCMN:新生代最小容量
NGCMX:新生代最大容量
NGC:当前新生代容量
S0CMX:最大幸存1区大小
S0C:当前幸存1区大小
S1CMX:最大幸存2区大小
S1C:当前幸存2区大小
ECMX:最大伊甸园区大小
EC:当前伊甸园区大小
YGC:年轻代垃圾回收次数
FGC:老年代回收次数
5)老年代垃圾回收统计 jstat -gcold 33133
$ jstat -gcold 33133
MC MU CCSC CCSU OC OU YGC FGC FGCT GCT
35416.0 33776.2 4608.0 4287.8 93184.0 16535.5 8 2 0.112 0.199
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
OC:老年代大小
OU:老年代使用大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
6)老年代内存统计 jstat -gcoldcapacity 33133
jstat -gcoldcapacity 33133
OGCMN OGCMX OGC OC YGC FGC FGCT GCT
87552.0 1398272.0 93184.0 93184.0 8 2 0.112 0.199
OGCMN:老年代最小容量
OGCMX:老年代最大容量
OGC:当前老年代大小
OC:老年代大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
7)元数据空间统计
$ jstat -gcmetacapacity 33133
MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC FGCT GCT
0.0 1081344.0 35416.0 0.0 1048576.0 4608.0 8 2 0.112 0.199
MCMN:最小元数据容量
MCMX:最大元数据容量
MC:当前元数据空间大小
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
8)总结:jstat -gc pid最为常用,可以看到相关的所有信息
jstack 命令
1)jstack pid 查看死锁
public class DeadLockTest {
private static Object lock1 = new Object();
private static Object lock2 = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lock1) {
try {
System.out.println("thread1 begin");
Thread.sleep(5000);
} catch (InterruptedException e) {
}
synchronized (lock2) {
System.out.println("thread1 end");
}
}
}).start();
new Thread(() -> {
synchronized (lock2) {
try {
System.out.println("thread2 begin");
Thread.sleep(5000);
} catch (InterruptedException e) {
}
synchronized (lock1) {
System.out.println("thread2 end");
}
}
}).start();
System.out.println("main thread end");
}
}
“Thread-1” 线程名
prio=5 优先级=5
tid=0x000000001fa9e000 线程id
nid=0x2d64 线程对应的本地线程标识nid
java.lang.Thread.State: BLOCKED 线程状态
还可以使用jvisualvm检测死锁。
2)线上CPU飙升了怎么办?jstack可以找出占用cpu最高的线程堆栈信息
- 使用命令top -p ,显示你的java进程的内存情况,pid是你的java进程号,比如19663
- 按H,获取每个线程的内存情况
- 找到内存和cpu占用最高的线程tid,比如19664(一个进程可能有多个线程)
- 转为十六进制得到 0x4cd0,此为线程id的十六进制表示
- 执行 jstack 19663|grep -A 10 4cd0,得到线程堆栈信息中 4cd0 这个线程所在行的后面10行,从堆栈中可以发现导致cpu飙高的调用方法
注:十六进制都是小写,找到该对应线程下文10行相关信息
nid=0x2d64 线程对应的本地线程标识nid
jvisualvm命令工具
1)导入dump文件进行分析
通过分析可以看到问题所在,char[]数组和String以及Long多,估计就是对象创建导致的,本身String底层就是Char数组。(实例则是上方堆内存dump)
2)检测死锁
实例见上文的jstack命令
3)抽样器使用
抽样器可以结合内存抽样和CPU抽样,结合一起找到问题。详情使用后期会针对实际例子来讲解。
总结
- 通过jmap查看JVM中各个区域的使用情况
- 通过jstack查看线程的运行情况,比如线程阻塞,死锁
- 通过jstat查看垃圾回收的情况,尤其fullgc,若频繁fullgc,就得发现问题解决问题,进行调优了。
本地能复现的问题我们可以通过命令或者jvisualvm来进行查看,线上我们则可以使用阿里开源工具Arthas,详情点击这里 实数太方便了。