问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

JVM基础参数设置与垃圾回收器的选择指南

创作时间:
作者:
@小白创作中心

JVM基础参数设置与垃圾回收器的选择指南

引用
CSDN
1.
https://blog.csdn.net/llg___/article/details/137103799

本文详细介绍了JVM的基础参数设置和垃圾回收器的选择,涵盖了多个JVM参数的解释,如-Xmx、-Xms、-XX:MaxMetaspaceSize等,以及如何选择合适的垃圾回收器。文章还提供了具体的调试思路和案例分析,具有较高的实用价值。

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、调优案例

案例背景:

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号