Jvm参数优化
- 背景
- 1. 系统上线规划容量
- - 分析
- 2. 垃圾回收器选择
- 吞吐量和响应时间
- 垃圾回收器选择
- 3. 规划各个分区的比例大小
- 4. 对象年龄对少移动到老年代合适
- 5. 对象多大放到老年代
- 6. 垃圾回收器CMS老年代参数优化
- 7. 配置OOM时的内存dump文件和GC日志
- 8. 通用JVM参数模板
背景
假设项目每天100w次登陆请求,一个服务器节点8G内存,如何设置JVM参数?
1. 系统上线规划容量
- 分析
- 100W请求登录峰值在早上,预估峰值每秒100次登录请求
- 假设部署3台服务器.每台处理30次请求,假设登录请求处理需要1s,JVM新生代里每秒就要产生30个登陆对象,1s之后请求完毕的这些对象成为垃圾.
- 一个登陆请求对象假设20个字段,一个对象估算500字节,30个登陆佔用大约15kb,考虑到RPC和DB操作,网络通信、写库、写缓存一顿操作下来,可以扩大到20-50倍,大约1s产生几百k-1M数据。
- 假设2C4G机器部署,分配2G堆内存,新生代则只有几百M,按照1s1M的垃圾产生速度,几百秒就会触发一次MinorGC了
- 假设4C8G机器部署,分配4G堆内存,新生代分配2G,如此需要几个小时才会触发一次MinorGC。
可以粗略的推断出来一个每天100w次请求的登录系统,按照4C8G的3实例集群配置,分配4G堆内存、2G新生代的JVM,可以保障系统的一个正常负载
2. 垃圾回收器选择
吞吐量和响应时间
吞吐量 = CPU在用户应用程序运行的时间 / (CPU在用户应用程序运行的时间 + CPU垃圾回收的时间)
响应时间 = 平均每次的GC的耗时
通常,吞吐优先还是响应优先这个在JVM中是一个两难之选,无法同时兼顾。
垃圾回收器选择
目前主流垃圾回收器CMS或者G1
G1是官方维护和推荐的垃圾回收器
系统对延迟敏感的推荐CMS
系统大内存,要求高吞吐的,采用G1回收器
3. 规划各个分区的比例大小
- 指定堆内存的大小,这是系统上线必须要做的,-Xms初始堆大小,-Xmx 最大堆大小,一般指定为系统内存的一半.
- 指定新生代大小-Xmn,官方推荐为3/8大小,根据业务场景去区分,无状态服务一般给到堆内存的3/4大小,有状态的服务(IM,网关)堆内存的1/3大小
- 栈内存-Xss,设置单个线程栈的大小,默认值和JDK版本有关,一般默认512-1024kb
如果采用正常的JVM参数配置如下
-Xms3072M
-Xmx3072M
-Xss1M
-XX:MetaspaceSize=256M
-XX:MaxMetaspaceSize=256M
-XX:SurvivorRatio=8
这样设置可能会由于动态对象年龄判断原则导致频繁full gc
压测过程中,短时间(比如20S后)Eden区就满了,此时再运行的时候对象已经无法分配,会触发MinorGC,
假设在这次GC后S1装入100M,马上过20S又会触发一次MinorGC,多出来的100M存活对象+S1区的100M已经无法顺利放入到S2区,此时就会触发JVM的动态年龄机制,将一批100M左右的对象推到老年代保存,持续运行一段时间,系统可能一个小时候内就会触发一次FullGC。
按照默认8:1:1的比例来分配时, survivor区只有 1G的 10%左右,也就是几十到100M.
将JVM参数更改为下面参数
-Xms3072M
-Xmx3072M
-Xmn2048M
-Xss1M
-XX:MetaspaceSize=256M
-XX:MaxMetaspaceSize=256M
-XX:SurvivorRatio=8
- 年轻代大小2G, eden与survivor的比例1.6g:0.2g
这样可以防止每次垃圾回收过后,survivor对象太早超过 50% ,
就可以降低因为对象动态年龄判断原则导致的对象频繁进入老年代的问题,
4. 对象年龄对少移动到老年代合适
如果对象这么长时间都没被回收,比如2分钟没有回收,可以认为这些对象是会存活的比较长的对象,从而移动到老年代,而不是继续一直占用survivor区空间。
默认值是15, 意味着对象要经过15次minor GC才会进入老年代
调整参数如下:
‐Xms3072M
‐Xmx3072M
‐Xmn2048M
‐Xss1M
‐XX:MetaspaceSize=256M
‐XX:MaxMetaspaceSize=256M
‐XX:SurvivorRatio=8
‐XX:MaxTenuringThreshold=5
5. 对象多大放到老年代
对象直接进入老年代参数-XX:PretenureSizeThreshold
‐Xms3072M
‐Xmx3072M
‐Xmn2048M
‐Xss1M
‐XX:MetaspaceSize=256M
‐XX:MaxMetaspaceSize=256M
‐XX:SurvivorRatio=8
‐XX:MaxTenuringThreshold=5
‐XX:PretenureSizeThreshold=1M
6. 垃圾回收器CMS老年代参数优化
JDK8默认的垃圾回收器是-XX:+UseParallelGC(年轻代)和-XX:+UseParallelOldGC(老年代).
如果内存比较大,建议使用G1
‐Xms3072M
‐Xmx3072M
‐Xmn2048M
‐Xss1M
‐XX:MetaspaceSize=256M
‐XX:MaxMetaspaceSize=256M
‐XX:SurvivorRatio=8
‐XX:MaxTenuringThreshold=5
‐XX:PretenureSizeThreshold=1M
‐XX:+UseParNewGC
‐XX:+UseConcMarkSweepGC
‐XX:CMSInitiatingOccupancyFraction=70
‐XX:+UseCMSInitiatingOccupancyOnly
‐XX:+AlwaysPreTouch
参数说明
1.‐Xms3072M ‐Xmx3072M 最小最大堆设置为3g,最大最小设置为一致防止内存抖动
2.‐Xss1M 线程栈1m
3.‐Xmn2048M ‐XX:SurvivorRatio=8 年轻代大小2g,eden与survivor的比例为8:1:1,也就是1.6g:0.2g:0.2g
4.-XX:MaxTenuringThreshold=5 年龄为5进入老年代 5.‐XX:PretenureSizeThreshold=1M 大于1m的大对象直接在老年代生成
6.‐XX:+UseParNewGC ‐XX:+UseConcMarkSweepGC 使用ParNew+cms垃圾回收器组合
7.‐XX:CMSInitiatingOccupancyFraction=70 老年代中对象达到这个比例后触发fullgc
8.‐XX:+UseCMSInitiatinpOccupancyOnly 老年代中对象达到这个比例后触发fullgc,每次
9.‐XX:+AlwaysPreTouch 强制操作系统把内存真正分配给IVM,而不是用时才分配。
7. 配置OOM时的内存dump文件和GC日志
增加了GC日志打印、OOM自动dump等配置内容
- dump配置
-XX:+HeapDumpOnOutOfMemoryError
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=${LOGDIR}/
- GC日志配置
-Xloggc:/log/xxx/gc.log
-XX:+PrintGCDateStamps
-XX:+PrintGCDetails
8. 通用JVM参数模板
- CMS回收器参数模板
-Xms4g
-Xmx4g
-Xmn2g
-Xss1m
-XX:SurvivorRatio=8
-XX:MaxTenuringThreshold=10
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=70
-XX:+UseCMSInitiatingOccupancyOnly
-XX:+AlwaysPreTouch
-XX:+HeapDumpOnOutOfMemoryError
-verbose:gc
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+PrintGCTimeStamps
-Xloggc:gc.log
- G1回收器参数模板
-Xms8g
-Xmx8g
-Xss1m
-XX:+UseG1GC
-XX:MaxGCPauseMillis=150
-XX:InitiatingHeapOccupancyPercent=40
-XX:+HeapDumpOnOutOfMemoryError
-verbose:gc
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+PrintGCTimeStamps
-Xloggc:gc.log