配置为:



- jdk1.8



- 服务器:4核8G



开篇:本篇仅以博主观念来讲解JVM一般会用到的参数,不做线上实例的讲解(因为展开太多了(╬▔皿▔)凸)。



 



首先上来就是一套稍微全一点的jvm参数配置(建议一般3G的堆大小即可,可根据实际情况调整(考虑到系统还有其他会用到内存、系统自己也会用内存)):


-Xms3072M -Xmx3072M -Xmn2048M 
  
 
  

   -Xss1M
  
 
  

   -XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=512M
  
 
  

   -XX:SurvivorRatio=8 
  
 
  

   -XX:MaxTenuringThreshold=15
  
 
  

   -XX:+UseParNewGC 
  
 
  

   -XX:+UseConcMarkSweepGC
  
 
  

   -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0
  
 
  

   -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/home/logs/xxx/gc.log
  
 
  

   -XX:+HeapDumpOnOutOfMemoryError 
  
 
  

   -XX:HeapDumpPath=/home/logs/xxx         // 这个目录路径可不加,若不加默认在tomcat目录下输出java_<pid>_<date>_<time>_heapDump.hprof



 



有了上面这套参数,那我们怎么配?



1 springboot应用配置方式(红色部分是jvm启动参数):


-Xms128m -Xmx256m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -XX:PretenureSizeThreshold=1M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/home/logs/hll-uc-service/gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/logs/hll-uc-service

 



2 tomcat应用配置方式:



需要编辑



# vim /home/tomcat/crm/bin/catalina.sh



-------------------------------------修改JAVA_OPTS如下--------------------------------------------------


JAVA_OPTS='-Xms32m -Xmx256m
  
 
  

   -Xss1M
  
 
  

   -XX:SurvivorRatio=8
  
 
  

   -XX:MaxTenuringThreshold=15
  
 
  

   -XX:PretenureSizeThreshold=1M
  
 
  

   -XX:+UseParNewGC
  
 
  

   -XX:+UseConcMarkSweepGC
  
 
  

   -XX:+UseCMSCompactAtFullCollection
  
 
  

   -XX:CMSFullGCsBeforeCompaction=0
  
 
  

   -XX:+PrintGCDetails
  
 
  

   -XX:+PrintGCTimeStamps
  
 
  

   -Xloggc:/home/logs/crm/gc.log
  
 
  

   -XX:+HeapDumpOnOutOfMemoryError
  
 
  

   -XX:HeapDumpPath=/home/logs/crm
  
 
  

   ..................................................'

-------------------------------------修改JAVA_OPTS如上--------------------------------------------------



 



这么多参数,是不是看着蒙蔽,下来一个个讲解下(还附带其他参数~)



 



参数

描述

-Xms3072M -Xmx3072M

设置堆大小为3G(含新生代+老年代),默认4C8G的机器用这个配置可以,具体要看系统可适量提高为4G

-Xmn2048M 

设置堆中新生代2G,3-2=1G,说明老年代只剩1G

-Xss1M

设置栈大小

-XX:SurvivorRatio=8

设置新生代比例 = Eden : S1 : S2 = 8 : 1: 1

-XX:MaxTenuringThreshold=15

设置分代年龄为15

-XX:PretenureSizeThreshold=1M

设置多大的对象可以直接进入老年代(这里是1M),默认值0,不管多大都先进eden。这个要合理设置,不过弄得老年代对象太多

-XX:+UseParNewGC

设置新生代使用ParNew垃圾回收器

-XX:+UseConcMarkSweepGC

设置老年代使用CMS垃圾回收器

-XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0

设置每多少次Full GC,老年代就触发一次整理,我这里设置为0表示触发一次就清除一次,好处是碎片得到整理,坏处是老年代GC变长。如果设置为10就10次触发一次整理,这样老年代碎片很多,很容易就触发Full GC

-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/home/logs/xxx/gc.log

设置GC日志打印,每次的新生代、老年代、元空间回收都会记录在gc.log,便于分析

-XX:+HeapDumpOnOutOfMemoryError  



-XX:HeapDumpPath=/home/logs/xxx

设置应用堆溢出的时候生成一个堆溢出快照,然后可以用MAT来进行分析

-XX:-+DisableExplicitGC

禁用System.gc();,因为调用该函数会触发full GC,但是有不少开源项目会使用Sysem.gc(),比如Netty,所以这个最好不开启,手动规范不乱使用System.gc()即可

-XX:CMSInitiatingOccupancyFaction=92

设置老年代空间占用到多大时触发Full GC,可不设置,默认就是92%

 



参数之间配合有什么效果?为什么要这样调(这里粗略过,展开讲就是另一篇文章)?



1 先举一个日活过亿的电商网站来讲解(先看完这个再继续看后面)


2 总结



从上面其实很简单看出GC的优化是什么?再根据实际场景中来尽量减少Full GC,仅触发young GC,因为young GC虽然也会造成停顿,但是停顿的时间很短,几ms~几十ms之间,所以几乎不对系统进行影响。但要达到这样的目的怎么做了?



很简单!尽量不触发old gc,可从如下几方面入手:



- 调大新生代比例,比如3G堆中给他2G新生代带下



- 调大Eden与S区的比例,因为假设一次存活对象太多了,然后放另一个清空好的S2区放不下,就会把垃圾放到老年代,所以需要调大比例



- 开启空间担保机制,这个从jdk1.6后就默认不用开启,所以参数说明我没列出来(假设jdk版本太低没开这个, 新生代 > 老年代剩余空间就会触发full gc,这样太坑了)



怎么观测上面调整后的参数效果?



1 java自带命令(如jstat),详情看如下文章(jstat -gc pid 5 5)

2 日志入手(如gc.log)

个人觉得可以不用这些的,用linux命令查看统计就够了(比如这样的)   -->  grep "PSYoungGen" gc.log | wc -l



 


GC时间嫌长?系统内存过大GC过久?实时性要求高?用G1试试!



1 G1相对于parNew + CMS有什么优势



G1是用于新生代老年代,最大的优点是最大预测停顿时间,G1是根据垃圾大概回收哪些时间最短来回收的,并且可设置为了达到这个目标需要回收多少次,默认八次。适用于大内存的应用,比如几十G,你新生代GC一次都不短时间了,所以这时候用G1最好。或者是要求是实时性高的都可以用G1