2.1 发现问题
2.1.1 jstat工具
jstat -gc 3785 1000 10
2.1.2 visualvm插件
2.1.3 Prometheus+Grafana
2.1.4 GC Viewer
生成GC日志
-XX:+PrintGCDetails -Xloggc:test1.log
生成
java -jar gcviewer-1.36.jar test1.log
2.1.5 GCeasy
https://gceasy.ycrash.cn/gc-dashboard.jsp
给的相当详细
2.2 常见GC模式
2.2.1 正常情况
2.2.2 缓存对象过多
2.2.3 内存泄漏
2.2.4 持续FullGC
2.2.5 元空间不足导致FullGC
2.3 解决GC问题的手段
2.3.1 优化基础JVM参数
2.3.1.1 -Xmx -Xms 堆参数
2.3.1.2 -XX:MaxMetaspaceSize -XX:MetaspaceSize 元空间参数
所以我们启动程序之后基本都会触发1到2次的由元空间不足引起的FullGC,这是因为-XX:MetaspaceSize
触发FullGC最开始的大小可能只有20M,所以会触发1到2次的FullGC,因为是在启动的时候触发的,所以并不会对用户的使用产生影响。
2.3.1.3 -Xss 虚拟机栈参数
2.3.1.4 不建议设置参数
2.3.1.5 模板
2.3.2 更换垃圾回收器
第二点减少对象的产生,就涉及内存调优,这个在以前已经讲过了,不再赘述
private Cache cache = Caffeine.newBuilder().weakKeys().softValues().build();
软引用+弱引用最大程度保证缓存不会溢出
先测试jdk8自带ps+po
-Xms4g -Xmx4g -Xss256k -XX:MaxMetaspaceSize=512m -XX:+DisableExplicitGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:/test.hprof -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
启动测试脚本
50并发下最大的响应时间是280ms,最后总结,并发越高,fullgc时间越长,因为创建对象速度快,可能刚刚释放,又要创建又要fullgc
再测试parnew+cms
-Xms4g -Xmx4g -Xss256k -XX:MaxMetaspaceSize=512m -XX:+DisableExplicitGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:/test.hprof -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseParNewGC -XX:+UseConcMarkSweepGC
50并发下最大的响应时间是220ms,其实都差不多
最后测试g1,在jdk17上
平均不到100ms,差距还是非常明显的。
2.4 实战
分析出有很多缓存对象
如果想要生成内存快照时不做fullgc,则需要通过jmap来保存,去掉live即可
jmap -dump:format=b,file=/home/jvm/dump/jvm-optimize-jmap.hprof 29317
但是mat分析时还是会自动把能回收的对象排除在外,所以我们需要
找到了482M的对象,本应该是被回收的,但是这些数组已经不在gcroot的引用链上了,所以用回溯找是没法找到的