GC调优
使用命令查看相关参数 java -XX:+PrintFlagsFinal -version | findstr “GC”
确定目标
【低延迟】还是【高吞吐量】,选择合适的回收器
CMS,G1,ZGC => 低延迟
ParallelGC =>高吞吐量
最快的 GC 是不发生 GC
查看 FullGC 前后的内存占用,考虑下面几个问题
- 数据是不是太多?
resultSet = statement.executeQuery(“select * from 大表 limit n”) - 数据表示是否太臃肿?
- 对象图
- 对象大小
- 是否存在内存泄漏?
- static Map map =
- 软引用
- 弱引用
- 第三方缓存实现 Redis
新生代调优
新生代的特点
- 所有的 new 操作的内存分配非常廉价
TLAB thread-local allocation buffer - 死亡对象的回收代价是零
- 大部分对象用过即死
- Minor GC 的时间远远低于 Full GC
在这里新生代是不是越大越好?
-Xmn 参数 ,设置新生代的大小
Sets the initial and maximum size (in bytes) of the heap for the young generation (nursery).GC is performed in this region more often than in other regions. If the size for the young generation is too small, then a lot of minor garbage collections are performed. If the size is too large, then only full garbage collections are performed, which can take a long time to complete. Oracle recommends that you keep the size for the young generation greater than 25% and less than 50% of the overall heap size.
为新生代设置堆的初始大小和最大大小(以字节为单位)。与其他区域相比,在该区域执行GC的频率更高。如果新生代的大小太小,则会执行大量小的垃圾收集。如果大小太大,则只执行完整的垃圾回收,这可能需要很长时间才能完成。Oracle建议将新生代的大小保持在总堆大小的25%和50%以下。
在这里大小的设置:
- 新生代能容纳所有【并发量 * (请求-响应)】的数据
- 幸存区大到能保留【当前活跃对象+需要晋升对象】
- 晋升阈值配置得当,让长时间存活对象尽快晋升
相关JVM参数
- -XX:MaxTenuringThreshold=threshold 设置最大阈值
- -XX:+PrintTenuringDistribution 打印相关信息
老年代调优
以 CMS 为例 低延迟高并发
- CMS 的老年代内存越大越好
- 先尝试不做调优,如果没有 Full GC 那么已经…,否则先尝试调优新生代
- 观察发生 Full GC 时老年代内存占用,将老年代内存预设调大 1/4 ~ 1/3
相关JVM参数 -XX:CMSInitiatingOccupancyFraction=percent 当老年代占用比例到达多少后出发Full GC进行回收
案例分析
案例1:请求高峰期发生 Full GC,单次暂停时间特别长 (在这里使用CMS)
答:在CMS当中的重新标记阶段会扫描整个的堆内存对象,在业务高峰期间,如果新生代对象过多,就会导致耗时过多,所以在重新标记之前对这个新生代当中的对象进行一次GC垃圾回收,就可以减少重新标记的时间。
案例2:老年代充裕情况下,发生 Full GC (CMS jdk1.7)
答:在jdk1.7以前的版本,由于使用的是永久代,当永久代设置过小就会导致发生 Full GC ,在jdk1.8之后,使用的是元空间,元空间使用的是操作系统的内存空间,就很难导致Full GC。
案例3:Full GC 和 Minor GC频繁
答:GC频繁,很显然是内存空间紧张,是由于新生代的空间设置过小,将新生代的空间调大,就可以避免新生代Minor GC ,同理,在新生代的内存空间足够的时候,晋升的对象就会变少,即Full GC也就不会被频繁触发了。