Jmap
jps 查出进程id
jmap 查看内存信息,实例个数以及占用内存大小
jmap -histo pid 查看历史生成的实例
jmap -histo pid >./test.txt 导出到文件
jmap -histo:live pid 查看当前存活的实例,执行过程中可能会触发一次full gc
jmap -heap pid 查看堆信息
jmap -dump:format=b,file=dump.hprof 在当前目录生成堆内存dump
也可以设置内存溢出自动导出dump文件(内存很大的时候,可能会导不出来)
- -XX:+HeapDumpOnOutOfMemoryError
- -XX:HeapDumpPath=./ (路径)
Jstack
jstack pid 查看进程里的线程情况
jstack找出占用cpu最高的线程堆栈信息
1,使用命令top -p ,显示你的java进程的内存情况,pid是你的java进程号
2,按H,获取每个线程的内存情况
3,找到内存和cpu占用最高的线程tid,比如1
4,转为十六进制得到 1,此为线程id的十六进制表示
5,执行 jstack pid |grep 1 -C 10 ,得到线程堆栈信息中 pid 这个线程所在行的前后10行,从堆栈中可以发现导致cpu飙高的调用方法
6,查看对应的堆栈信息找出可能存在问题的代码
Jinfo
jinfo -flags pid 查看jvm的参数
jinfo -sysprops pid 查看java系统参数
java -XX:+PrintFlagsInitial 表示打印出所有参数选项的默认值
java -XX:+PrintFlagsFinal 表示打印出所有参数选项在运行程序时生效的值
Jstat
jstat命令可以查看堆内存各部分的使用量,以及加载类的数量。命令的格式如下:
jstat [-命令选项] [vmid] [间隔时间(毫秒)] [查询次数]
注意:使用的jdk版本是jdk8
jstat -gc pid 最常用,可以评估程序内存使用及GC压力整体情况
- S0C:第一个幸存区的大小,单位KB
- S1C:第二个幸存区的大小
- S0U:第一个幸存区的使用大小
- S1U:第二个幸存区的使用大小
- EC:伊甸园区的大小
- EU:伊甸园区的使用大小
- OC:老年代大小
- OU:老年代使用大小
- MC:方法区大小(元空间)
- MU:方法区使用大小
- CCSC:压缩类空间大小
- CCSU:压缩类空间使用大小
- YGC:年轻代垃圾回收次数
- YGCT:年轻代垃圾回收消耗时间,单位s
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间,单位s
- GCT:垃圾回收消耗总时间,单位s
jstat -gcutil pid 查看各个区域使用比例
- S0:幸存1区当前使用比例
- S1:幸存2区当前使用比例
- E:伊甸园区使用比例
- O:老年代使用比例
- M:元数据区使用比例
- CCS:压缩使用比例
- YGC:年轻代垃圾回收次数
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间
- GCT:垃圾回收消耗总时间
JVM运行情况预估
可以先设置一些初始性的jvm参数,比如:
堆内存大小 -Xms -Xmx
年轻代的大小 -Xmn
Eden和Survivorq区的比例 -XX:SurvivorRatio
老年代的大小
大对象的阈值 -XX:PretenureSizeThreshold
大龄对象进入老年代的阈值 -XX:MaxTenuringThreshold
年轻代对象增长速率
执行 jstat -gc pid 1000 10 观察每秒钟eden新增多少对象,打印10次
Young GC的触发频率和每次耗时
根据年轻代的对象增长速率以及设置的年轻代大小可以算出多久触发一次Young GC,每次耗时可以通过jstat 里的 YGCT/YGC 计算得出,根据上面的结果可以知道多久会因为Young GC 卡顿多久
每次Young GC 后有多少对象存活和进入老年代
之前算出了Young GC的触发频率,假设10分钟一次,可以执行 jstat -gc pid 600000 10 查看每次Young GC后eden,survivor和老年代的使用情况,一般gc后eden区会大幅减少,survivor和老年代会增长,增长的对象就是每次Young GC后存活对象,老年代的增长就是每次Young GC后进入老年代的对象,可以大概推算出老年代的增长频率。
Full GC的触发频率和每次耗时
根据老年代的增长频率以及设置的老年代大小可以算出触发频率,FGCT/FGC可以得出每次耗时
优化思路
让每次Young GC后的存活对象小于Survivor的50%【1】,都留存在年轻代里,尽量别让对象进入老年代,减少Full GC频率,避免Full GC对JVM性能的影响。
【1】对象动态年龄判断
Young gc,把eden区和survivor区垃圾对象回收,把剩余存活的对象一次性挪动到另外一块为空的survivor区。当前放对象的Survivor区域里(其中一块区域,放对象的那块s区),一批对象的总大小大于这块Survivor区域内存大小的50%(-XX:TargetSurvivorRatio可以指定),那么此时大于等于这批对象年龄最大值的对象,就可以直接进入老年代了,例如Survivor区域里现在有一批对象,年龄1+年龄2+年龄n的多个年龄对象总和超过了Survivor区域的50%,此时就会把年龄n(含)以上的对象都放入老年代。这个规则其实是希望那些可能是长期存活的对象,尽早进入老年代。对象动态年龄判断机制一般是在Young gc之后触发的。
其余的对象进入老年代的机制
大对象直接进入老年代
大对象就是需要大量连续内存空间的对象(比如:字符串、数组)。JVM参数
-XX:PretenureSizeThreshold 可以设置大对象的大小,如果对象超过设置大小会直接进入老年代,不会进入年轻代,这个参数只在 Serial 和ParNew两个收集器下有效。
长期存活的对象将进入老年代
如果对象在 Eden 出生并经过第一次 Minor GC 后仍然能够存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并将对象年龄设为1。对象在 Survivor 中每熬过一次 MinorGC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁,CMS收集器默认6岁)就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数
-XX:MaxTenuringThreshold 来设置。
触发Full GC
老年代空间分配担保机制
年轻代每次minor gc之前JVM都会计算下老年代剩余可用空间
如果这个可用空间小于年轻代里现有的所有对象大小之和(包括垃圾对象)
就会看一个“-XX:-HandlePromotionFailure”(jdk1.8默认就设置了)的参数是否设置了
如果有这个参数,就会看看老年代的可用内存大小,是否大于之前每一次minor gc后进入老年代的对象的平均大小。
如果上一步结果是小于或者之前说的参数没有设置,那么就会触发一次Full gc,对老年代和年轻代一起回收一次垃圾,如果回收完还是没有足够空间存放新的对象就会发生"OOM"
当然,如果minor gc之后剩余存活的需要挪动到老年代的对象大小还是大于老年代可用空间,那么也会触发full gc,full gc完之后如果还是没有空间放minor gc之后的存活对象,则也会发生“OOM”
Arthas简单使用
# github下载arthas
wget https://alibaba.github.io/arthas/arthas-boot.jar
java -jar arthas-boot.jar 运行
1.选择进程序号1,进入进程信息操作
2.输入dashboard可以查看整个进程的运行情况,线程、内存、GC、运行环境信息
3.输入thread可以查看线程详细情况
4.输入 thread加上线程ID 可以查看线程堆栈
5.输入 jad加类的全名 可以反编译
ognl 命令 查看变量修改变量
GC日志
打印GC日志
在JVM参数里增加参数,%t 代表时间
-Xloggc:./gc-%t.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps
-XX:+PrintGCTimeStamps -XX:+PrintGCCause
-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M
CMS 打印日志
-Xloggc:./gc-cms-%t.log -Xms100M -Xmx100M -XX:MetaspaceSize=256M
-XX:MaxMetaspaceSize=256M -XX:+PrintGCDetails -XX:+PrintGCDateStamps
-XX:+PrintGCTimeStamps -XX:+PrintGCCause -XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC
G1打印日志
-Xloggc:./gc-g1-%t.log -Xms50M -Xmx50M -XX:MetaspaceSize=256M
-XX:MaxMetaspaceSize=256M -XX:+PrintGCDetails -XX:+PrintGCDateStamps
-XX:+PrintGCTimeStamps -XX:+PrintGCCause -XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M -XX:+UseG1GC
分析日志可以使用一些网站比如:https://gceasy.io