JVM自带工具
jmap 查看内存信息
#查看历史生成的实例
jmap -histo [进程id]
#查看当前存活的实例
jmap -histo:live [进程id]
#导出文件
jmap ‐dump:format=b,file=[文件名称] [进程id]
可以用jvisualvm命令工具导入该dump文件分析
jstack
#查看堆信息,可以检查死锁
jmap [进程id]
通过jstack找出占用cpu最高的线程堆栈信息
#jps 查找出进程id
top -p [进程id]
# 按H,获取每个线程的内存情况,找到线程id
# 把线程id的十六进制表示
# 得到线程堆栈信息中 4cd0 这个线程所在行的后面10行,从堆栈中可以发现导致cpu飙高的调用方法
jstack [进程id]|grep -A 10 4cd0
#查看对应的堆栈信息找出可能存在问题的代码
jinfo
#查看正在运行的Java应用程序的扩展参数
jinfo -flags 12128
#查看java系统参数
jinfo -sysprops 248821
jstat
#①jstat -gc pid 可以评估程序内存使用及GC压力整体情况 各个参数含义如下:
#- 13456 - 指定 Java 进程 ID 为 13456 的进程
#- 2000 - 每隔 2000毫秒采样一次
#- 10000 - 采样 10000 次
jstat -gc 13456 2000 10000
#堆内存统计
jstat -gccapacity 248821
#新生代垃圾回收统计
jstat -gcnew 248821
#新生代内存统计
jstat -gcnewcapacity 248821
#老年代垃圾回收统计
jstat -gcold 248821
#老年代内存统计
jstat -gcoldcapacity 248821
#元数据空间统计
jstat -gcmetacapacity 248821
#用于显示 Java 堆内存各个区域的使用情况百分比
jstat -gcutil 248821
①垃圾回收统计
| S0C | S1C | S0U | S1U | EC | EU | OC | OU | MC | MU | CCSC | CCSU | YGC | YGCT | FGC | FGCT | GCT |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 幸存区1的大小 | 幸存区2的大小 | 幸存区1使用 | 幸存区2使用 | 伊甸园区 | 伊甸园区使用 | 老年代 | 老年代使用 | 方法区大小(元空间) | 方法区使用 | 压缩类空间大小 | 压缩类空间使用 | 年轻代垃圾回收次数 | 年轻代垃圾回收消耗时间 | 老年代垃圾回收次数 | 老年代垃圾回收消耗时间 | 垃圾回收消耗总时间 |
②堆内存统计
| NGCMN | NGCMX | NGC | S0C | S1C | EC | OGCMN | OGCMX | OGC | OC | MCMN | MCMX | MC | CCSMN | CCSMX | CCSC | YGC | FGC | CGC |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 新生代最小容量 | 新生代最大容量 | 当前新生代容量 | 幸存区1的大小 | 幸存区2的大小 | 伊甸园区 | 老年代最小容量 | 老年代最大容量 | 当前老年代大小 | 当前老年代大小 | 最小元数据容量 | 最大元数据容量 | 当前元数据空间大小 | 最小压缩类空间大小 | 最大压缩类空间大小 | 当前压缩类空间大小 | 年轻代gc次数 | 老年代GC次数 | |
③新生代垃圾回收统计
| S0C | S1C | S0U | S1U | TT | MTT | DSS | EC | EU | YGC | YGCT |
|---|---|---|---|---|---|---|---|---|---|---|
| 对象在新生代存活的次数 | 对象在新生代存活的最大次数 | 期望的幸存区大小 | 年轻代垃圾回收次数 | 年轻代垃圾回收消耗时间 | ||||||
④新生代内存统计
| NGCMN | NGCMX | NGC | S0CMX | S0C | S1CMX | S1C | ECMX | EC | YGC | FGC |
|---|---|---|---|---|---|---|---|---|---|---|
| 新生代最小容量 | 新生代最大容量 | 当前新生代容量 | 最大幸存1区大小 | 最大幸存2区大小 | 当前幸存2区大小 | 最大伊甸园区大小 | 当前伊甸园区大小 | 老年代回收次数 | ||
⑤老年代垃圾回收统计
| MC | MU | CCSC | CCSU | OC | OU | YGC | FGC | FGCT | CGC | CGCT | GCT |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 方法区大小 | 方法区使用大小 | 压缩类空间大小 | 压缩类空间使用大小 | 老年代大小 | 老年代使用大小 | 年轻代垃圾回收次数 | 老年代垃圾回收次数 | 老年代垃圾回收消耗时间 | 垃圾回收消耗总时间 | ||
⑥老年代内存统计
| OGCMN | OGCMX | OGC | OC | YGC | FGC | FGCT | CGC | CGCT | GCT |
|---|---|---|---|---|---|---|---|---|---|
| 老年代最小容量 | 老年代最大容量 | 当前老年代大小 | 老年代大小 | 年轻代垃圾回收次数 | 老年代垃圾回收次数 | 老年代垃圾回收消耗时间 | 垃圾回收消耗总时间 | ||
⑦元数据空间统计
| MCMN | MCMX | MC | CCSMN | CCSMX | CCSC | YGC | FGC | FGCT | CGC | CGCT | GCT |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 最小元数据容量 | 最大元数据容量 | 当前元数据空间大小 | 最小压缩类空间大小 | 最大压缩类空间大小 | 当前压缩类空间大小 | 年轻代垃圾回收次数 | 老年代垃圾回收次数 | 老年代垃圾回收消耗时间 | 当前压缩类空间的容量 | 当前压缩类空间总容量 | 垃圾回收消耗总时间 |
⑧堆内存各个区域的使用情况百分比
| S0 | S1 | E | O | M | CCS | YGC | FGC | FGCT | GCT |
|---|---|---|---|---|---|---|---|---|---|
| 幸存1区当前使用比例 | 幸存2区当前使用比例 | 伊甸园区使用比例 | 老年代使用比例 | 元数据区使用比例 | 压缩使用比例 | 年轻代垃圾回收次数 | 老年代垃圾回收次数 | 老年代垃圾回收消耗时间 | 垃圾回收消耗总时间 |
调优分析
JVM运行情况预估
先给自己的系统设置一些初始性的JVM参数,比如堆内存大小,年轻代大小,Eden和Survivor的比例,老年代的大小,大对象的阈值,大龄对象进入老年代的阈值等
- 年轻代对象增长的速率
- 通过 jstat -gc pid 1000 10 观察EU估算每秒新增多少对象,如果系统负载不高,可以更换时间频率
- Young GC的触发频率和每次耗时
- 根据EU大小和新增速率 推算出YGC大概多久触发一次,YGC的平均耗时 = YGCT/YGC,系统大概多久会因为YGC的执行而卡顿多久。
- 每次Young GC后有多少对象存活和进入老年代
- 这个因为之前已经大概知道Young GC的频率,假设是每5分钟一次,那么可以执行命令 jstat -gc pid 300000 10 ,观察每次结果eden,survivor和老年代使用的变化情况,在每次gc后eden区使用一般会大幅减少,survivor和老年代都有可能增长,这些增长的对象就是每次Young GC后存活的对象,同时还可以看出每次Young GC后进去老年代大概多少对象,从而可以推算出老年代对象增长速率。
- Full GC的触发频率和每次耗时
- 知道了老年代对象的增长速率就可以推算出Full GC的触发频率了,Full GC的每次耗时可以用公式 FGCT/FGC 计算得出。
优化思路其实简单来说就是尽量让每次Young GC后的存活对象小于Survivor区域的50%,都留存在年轻代里。尽量别让对象进入老年代。尽量减少Full GC的频率,避免频繁Full GC对JVM性能的影响。
full gc比minor gc还多的原因有哪些
- 元空间不够导致的多余full gc
- 显示调用System.gc()造成多余的full gc,这种一般线上尽量通过-XX:+DisableExplicitGC参数禁用,如果加上了这个JVM启动参数,那么代码中调用System.gc()没有任何效果
- 老年代空间分配担保机制
内存泄露
JVM级缓存就简单使用一个hashmap,于是不断往里面放缓存数据,但是很少考虑这个map的容量问题,结果这个缓存map越来越大,一直占用着老年代的很多空间,时间长了就会导致full gc非常频繁,这就是一种内存泄漏,对于一些老旧数据没有及时清理导致一直占用着宝贵的内存资源,时间长了除了导致full gc,还有可能导致OOM。
这种情况完全可以考虑采用一些成熟的JVM级缓存框架来解决,比如ehcache等自带一些LRU数据淘汰算法的框架来作为JVM级的缓存。