文章目录
- 1、-Xmx 和 –Xms
- 2、-XX:MaxMetaspaceSize 和 –XX:MetaspaceSize
- 3、-Xss
- 4、不建议改的参数
- 5、其他参数
- 6、选择GC回收器的调试思路
- 7、CMS的并发模式失败现象的解决
- 8、调优案例
GC问题解决方式:
- 优化JVM基础参数,避免频繁Full GC
- 减少对象的产生,以免对象产生速度过快导致频繁Full GC
- 选择适合业务场景的垃圾回收器
- 优化垃圾回收器的参数
1、-Xmx 和 –Xms
-Xmx设置最大堆内存(max),-Xms设置可用堆内存大(total)
计算理论最大可用堆空间,如服务器内存4G,操作系统自己使用的内存+元空间最大值+其它软件占用1.5G ⇒ 理论最大可用堆空间为2.5g,只是理论值。(减去元空间是因为,Java 8及以后,元空间使用的是直接内存)
最后设置的堆内存大小,应是按照系统最大并发估计,且小于上面的理论值。最后,将-Xms设置的和-Xmx一样大,理由:
- 可用内存一开始就等于最大堆内存,避免堆扩容时频繁向操作系统申请内存,影响程序性能
- 避免扩容时,因其他应用占用了操作系统内存而申请扩容失败
- 服务启动速度更快,初始堆太小(Xms太小),Java应用启动会变慢,因为JVM会被迫频繁垃圾回收,直到堆增长到一个合理的大小
2、-XX:MaxMetaspaceSize 和 –XX:MetaspaceSize
-XX:MaxMetaspaceSize :最大元空间。默认值比较大,万一元空间内存泄漏,会影响到操作系统(因为元空间用的直接内存)
–XX:MetaspaceSize:到这个值后,触发Full GC,后续什么值再触发,JVM自行计算。
3、-Xss
指定每个栈的大小,不指定取默认值,默认值取决于操作系统。如Linux x86 64bit 默认是1MB。如果没有方法的递归调用,可调小栈大小
//合理值为256k – 1m之间
-Xss256k
4、不建议改的参数
以下参数,调整可能会让某一个接口得益,但同时也会影响其他接口,不建议修改。
1)-Xmn:年轻代的大小
年轻代的大小,默认为整个堆的1/3。可根据系统峰值计算年轻代大小,以尽量让对象只存在年轻代,不进入老年代(这样后面Young GC一下就行),但计算这个值的影响因素太多,不建议改。且G1垃圾回收器会动态调整年轻代的大小,更不建议改。
2)‐XX:SurvivorRatio 伊甸园区和幸存者区的大小比例,默认值为8。
3)‐XX:MaxTenuringThreshold 最大晋升阈值
对象晋升有两个情况:
- (GC一次,对象年龄+1)年龄 > 此值,进入老年代
- 动态年龄判断机制:按年龄从小到大将对象空间加起来, > survivor区域的50%,就把大于等于该年龄的对象晋升到老年代
5、其他参数
-XX:+DisableExplicitGC作用:禁止在代码中调用System.gc()
-XX:+HeapDumpOnOutOfMemoryError作用:OOM时,生成hprof内存快照文件
-XX:HeapDumpPath=<path>作用:指定内存快照文件的生成路径
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:文件路径作用:JDK8及之前,打印GC日志
-Xlog:gc*:file=文件路径作用:JDK9及以后,打印GC日志
JVM参数模板总结:
-Xms1g
-Xmx1g
-Xss256k
-XX:MaxMetaspaceSize=512m
-XX:+DisableExplicitGC
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/opt/logs/my-service.hprof
# <=JDK8
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps# >=JDK9
-Xloggc:文件路径
6、选择GC回收器的调试思路
用以下代码模拟系统的业务代码进行压测:
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.SneakyThrows;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;@RestController
@RequestMapping("/fullgc")
public class DemoController {private Cache cache = Caffeine.newBuilder().weakKeys().softValues().build();private List<Object> objs = new ArrayList<>();private static final int _1MB = 1024 * 1024;//FULLGC测试结果://ps + po 50并发 260ms 100并发 474ms 200并发 930ms//cms -XX:+UseParNewGC -XX:+UseConcMarkSweepGC 50并发 157ms 200并发 833ms//g1 JDK11 并发200 248@GetMapping("/1")public void test() throws InterruptedException {cache.put(RandomStringUtils.randomAlphabetic(8),new byte[10 * _1MB]);}}
创建一个具有弱引用键(value不存在了,可被GC回收)和软引用值的缓存对象(内存不足时,可GC回收),接口往里面放10M的数据,如此,不会OOM,且会较多触发FULL GC。用Jmeter并发/1接口的同时,再调用一次接口/2,用这个接口模拟有突发大对象产生时,对系统响应时间的影响:
@GetMapping("/2")
public void test() throws InterruptedException {ArrayList<Object> objects = new ArrayList<>();for (int i = 0; i < 1024; i++) {objects.add(new byte[3 * _1MB]);}
}
步骤:
- Jmeter脚本压测,添加RT响应时间组件
- 选择不同的垃圾回收器组合,测试50、100、200并发下,FULL GC对RT的时间的影响
统一JVM参数设置:
-Xms8g -Xmx8g -Xss256k -XX:MaxMetaspaceSize=512m -XX:+DisableExplicitGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:/test.hprof -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
测试的垃圾回收器组合:
- ps + po
- cms
- JDK11的默认回收器g1
测试场景:
- 高并发(/1接口)
- 大对象产生(/2接口)
观察RT的结果:查看最大值,即接口的最大响应时间。峰值的出现,即FULL GC对接口响应时间的影响
7、CMS的并发模式失败现象的解决
CMS的垃圾清理线程和用户线程并行
如果并行清理的过程中,老年代的空间,不足以容纳新晋升到老年代的对象,就发生并发模式失败:
出现并发模式失败时,会导致JVM使用Serial Old单线程进行FULLGC回收老年代,这样会产生较长时间的停顿,从而影响接口响应时间。解决思路:
- 减少对象的产生以及晋升
- 增加堆内存大小
-XX:CMSInitiatingOccupancyFraction=
值
关于垃圾回收器的参数CMSInitiatingOccupancyFraction,当老年代大小达到其值,会自动进行CMS老年代垃圾回收。JDK8中,该值为-1,计算公式:
((100 - MinHeapFreeRatio) + (double)(CMSTriggerRatio * MinHeapFreeRatio) / 100.0)
最后,这个参数想生效,必须先开启:
-XX:+UseCMSInitiatingOccupancyOnly
调小这个值,比如从默认的90%调到60%,早些进行老年代的回收,就不会出现并发模式失败,也就不会有后面的Serial Old单线程回收老年代。
8、调优案例
案例背景:
- 系统的接口在平时响应较快,但高峰期会出现调用时间较长的现象,现需要优化性能
分析的方向:
- GC问题:查看是否出现连续的Full GC或者单次GC时间过长
- 内存问题
步骤:
- 生成GC报告,GcEasy分析
- GC有问题,就调整参数或者换垃圾回收器
- 内存有问题就jmap或Arthas将堆内存快照保存
- MAT或者heaphero在线分析内存
- 修复
关于heaphero:https://heaphero.io/
调优案例:https://www.bilibili.com/video/BV1r94y1b7eS?p=68&vd_source=d86e858b4dfd8944a691759448d35279