写在前面
jdk9以及之后的版本已经将默认的垃圾收集器parallel更换为G1.本文就一起来看下。
1:G1介绍
parallel GC的设计目标是高吞吐量,CMS GC的设计目标是低延迟,而G1的设计目标不是这二者中的任何一个,其设计目标是让GC的STW时间变的可控和可配置,具体的实现方案是将内存划分为多个region,默认是2048个,每个region可能是old区,eden区,也可能是survivor区,并且随着程序的运行可能会随时发生转变,如下图:
每次GC时会处理所有的年轻代(eden region和survivor region),以及部分的老年代,这些需要垃圾回收的region叫做回收集(collection set),如下图打对号的:
2:G1重要参数
- -XX:+UseG1GC
启用G1 GC - -XX:G1NewSizePercent
初始年轻代占用整个堆的百分比,默认时5% - -XX:G1MaxNewSizePercent
年轻代占用堆最大的百分比,默认是60% - -XX:G1HeapRegionSize
设置region的大小,单位是MB,需要设置为2的次幂值,当大对象无法分配时可以适当将该值调大 - -XX:ConcGCThreads
与Java应用一起执行的GC线程数量,该值越低则系统的吞吐量越大,但过低会导致GC时间过长 - -XX:InitiatingHeapOccupancyPercent
当老年代垃圾达到指定百分比时,开始老年代的并行回收。即该值决定了什么时候启动老年代GC,默认时45% - -XX:G1HeapWastePercent
当垃圾占用百分比达到多少时,停止GC,默认是5%,即每次GC并不会回收所有的垃圾对象,部分垃圾对象会留到之后的GC进行回收 - -XX:GCTimeRatio
设置GC占用的CPU时间和工作线程占用CPU时间的比例,计算公式100/(1+GCTimeRatio)
,G1默认值为9,则10%的时间会用在GC上,parallel默认值是99,则1%的时间会用在GC上 - -XX:MaxGCPauseMillis
设置每次GC的暂停时间,单位毫秒,G1会尽量维持在这个时间,但不保证绝对是,即如果设置50ms,最终GC时间100,200ms都是有可能的
3:G1的不足
当在某些情况下G1触发了FULL GC,将会退化为serial GC使用单线程的方式来进行垃圾回收,可能发生原因以及处理办法如下。
3.1:老年代被填满
这种情况一般是因为参与GC的线程数过少,无法快速的进行垃圾回收,导致老年代填满,解决办法是通过参数-XX:ConcGCThreads调大参与GC的线程数
3.2:巨型对象分配失败
对象过大,无法找到一个可用的region,则会触发FULL GC,解决办法是通过参数-XX:G1HeapRegionSize调大region的大小,或者是增加整个堆内存大小,从而间接增大单个region的大小
4:G1参数配置
测试使用的jar从这里 下载。
首先使用G1相关的配置启动程序,如下:
bogon:~ xb$ java -version
java version "9.0.4"
Java(TM) SE Runtime Environment (build 9.0.4+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.4+11, mixed mode)bogon:temp xb$ java -Xmx1g -Xms1g -XX:+UseG1GC -XX:-UseAdaptiveSizePolicy -XX:MaxGCPauseMillis=200 -jar gateway-server-0.0.1-SNAPSHOT.jar . ____ _ __ _ _/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/ ___)| |_)| | | | | || (_| | ) ) ) )' |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot :: (v2.0.4.RELEASE)
查看堆快照:
bogon:temp xb$ jps -l
1939 jdk.jcmd/sun.tools.jps.Jps
1576 gateway-server-0.0.1-SNAPSHOT.jarbogon:temp xb$ jhsdb jmap --heap --pid 1576
Attaching to process ID 1576, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 9.0.4+11using thread-local object allocation.
Garbage-First (G1) GC with 4 thread(s) -- 使用G1 GC,4个GC线程Heap Configuration:MinHeapFreeRatio = 40MaxHeapFreeRatio = 70MaxHeapSize = 1073741824 (1024.0MB) -- 最大堆内存大小NewSize = 1363144 (1.2999954223632812MB) -- 新生代初始大小1MMaxNewSize = 643825664 (614.0MB) -- 新生代最大大小614mOldSize = 5452592 (5.1999969482421875MB) 初始old区大小5mNewRatio = 2 young:old=1:2SurvivorRatio = 8 eden:s0:s1=8:1:1MetaspaceSize = 21807104 (20.796875MB)CompressedClassSpaceSize = 1073741824 (1024.0MB)MaxMetaspaceSize = 17592186044415 MB -- 最大metaspace无限大,天文数字,即不限制G1HeapRegionSize = 1048576 (1.0MB) -- 每个region的大小1m,没有设置是默认大小Heap Usage:
G1 Heap: -- 堆配置(young+Old)regions = 1024 -- 一共1024个region,每个regsion 1m,所以总大小为1gcapacity = 1073741824 (1024.0MB) -- 总大小1gused = 47431216 (45.23393249511719MB) -- 已使用堆大小45mfree = 1026310608 (978.7660675048828MB) -- 空闲堆大小978m4.417376220226288% used -- 使用量为4.4%
G1 Young Generation: -- 堆内存年轻代配置
Eden Space: -- young的edenregions = 23 -- 一共23个regioncapacity = 53477376 (51.0MB) -- 容量是51mused = 24117248 (23.0MB) -- 使用了23mfree = 29360128 (28.0MB) -- 空闲28m45.09803921568628% used -- 使用率为45%
Survivor Space: -- 存活区regions = 4 -- 一共4个regioncapacity = 4194304 (4.0MB) -- 总大小4mused = 4194304 (4.0MB) -- 已使用4mfree = 0 (0.0MB) -- 空闲0m100.0% used -- 使用率100%
G1 Old Generation: -- 堆内存老年代regions = 19 -- 一共19个regioncapacity = 39845888 (38.0MB) -- 总容量38mused = 19119664 (18.233932495117188MB) -- 已使用约18mfree = 20726224 (19.766067504882812MB) -- 空暇约19m47.98403288188734% used -- 使用率约48%22285 interned Strings occupying 2350032 bytes.