文章收录在网站:http://hardyfish.top/
文章收录在网站:http://hardyfish.top/
文章收录在网站:http://hardyfish.top/
文章收录在网站:http://hardyfish.top/
G1收集器
G1**(Garbage First)是一款面向服务器的垃圾收集器,主要针对配置多核处理器以及大容量内存的机器,满足GC停顿时间要求的同时,还具备高吞吐量性能特征**。
- JDK 9 开始默认使用G1 垃圾收集器。
内存划分
G1将堆划分为多个大小相等的独立的
Region
区域。一般
Region
区的大小等于堆空间的总大小除以2048,比如目前的堆空间总大小为8GB,就是8192MB/2048=4MB
,那么最终每个Region
区的大小为4MB
。
- 也可以用参数
-XX:G1HeapRegionSize
强制指定每个Region
区的大小。
新生代和老年代
默认新生代对堆内存的初始占比是5%,如果堆大小为8GB,那么年轻代占据
400MB
左右的内存,对应大概是100个Region
区,可以通过-XX:G1NewSizePercent
设置新生代初始占比。在Java程序运行中,JVM会不停的给新生代增加更多的
Region
区,但是最多新生代的占比不会超过堆空间总大小的60%,可以通过-XX:G1MaxNewSizePercent
调整。新生代中的
Eden
区和Survivor
区对应的Region
区比例也跟之前一样,默认8:1:1,假设新生代现在有400个Region
,那么整个新生代的占比则为Eden=320,S0/From=40,S1/To=40
。
G1中的年老代晋升条件和之前的无差,达到年龄阈值的对象会被转入年老代的
Region
区中,不同的是对于大对象的分配,在G1中不会让大对象进入年老代,在G1中由专门存放大对象的Region
区叫做Humongous
区,如果在分配对象时,判定出一个对象属于大对象,那么则会直接将其放入Humongous
区存储。
在G1中,判定一个对象是否为大对象的方式为:
对象大小是否超过单个普通
Region
区的50%,如果超过则代表当前对象为大对象,那么该对象会被直接放入Humongous
区。
- 比如:目前是8GB的堆空间,每个
Region
区的大小为4MB
,当一个对象大小超过2MB
时则会被判定为属于大对象。如果程序运行过程中出现一个巨型对象,当一个
Humongous
区存不下时,可能会横跨多个Region
区存储它。
Humongous
区存在的意义:
可以避免一些短命的巨型对象直接进入年老代,节约年老代的内存空间,可以有效避免年老代因空间不足时的GC开销。
当堆空间发生全局GC(
FullGC
)时,除开回收新生代和年老代之外,也会对Humongous
区进行回收。
什么场景下适合采用G1收集器的建议
堆空间内
50%
以上的内存会被存活占用的应用。分配速度和晋升速度特别快的应用。
至少
8GB
以上堆内存的应用。采用原本分代收集器GC时间会长达
1s+
的应用。追求停顿时间在
500ms
以内的应用。
GC类型
YoungGC:
在G1中,当新生代区域被用完时,G1首先会大概计算一下回收当前的新生代空间需要花费多少时间,如果回收时间远远小于参数
-XX:MaxGCPauseMills
设定的值,那么不会触发YoungGC
,而是会继续为新生代增加新的Region
区用于存放新分配的对象实例。
- 用户未显式通过
-XX:MaxGCPauseMills
参数设定GC预期回收停顿时间值,那么G1默认为200ms
。直至某次
Eden
区空间再次被放满并经过计算后,此次回收的耗时接近-XX:MaxGCPauseMills
参数设定的值,那么才会触发YoungGC
。当
YoungGC
被触发时,首先会将目标Region
区中的存活对象移动至幸存区空间(Survivor-From
区标志的Region
)。同时达到晋升年龄标准的对象也会被移入至年老代
Region
中存储。
- G1收集器在发生
YoungGC
时,复制移动对象时是采用的多线程并行复制,以此来换取更优异的GC性能。
MixedGC:
当整个堆中年老代的区域占有率达到参数
-XX:InitiatingHeapOccupancyPercent
设定的值后触发MixedGC
,发生该类型GC后,会回收所有新生代Region
区、部分年老代Region
区(会根据期望的GC停顿时间选择合适的年老代Region
区优先回收)以及大对象Humongous
区。正常情况下,G1垃圾收集时会先发生
MixedGC
,主要采用复制算法,在GC时先将要回收的Region
区中存活的对象拷贝至别的Region
区内,拷贝过程中,如果发现没有足够多的空闲Region
区承载拷贝对象,此时就会触发一次Full GC
。
FullGC:
当整个堆空间中的空闲
Region
不足以支撑拷贝对象或由于元数据空间满了等原因触发,在发生FullGC
时,G1首先会停止系统所有用户线程,然后采用单线程进行标记、清理和压缩整理内存,以便于清理出足够多的空闲Region
来供下一次MixedGC
使用。但该过程是单线程串行收集的,因此这个过程非常耗时(
ShenandoahGC
中采用了多线程并行收集)。
- G1收集器中并没有FullGC,,G1中的FullGC是采Sserial old FullGC。
GC过程
G1收集器在发生GC时执行过程大致会分为四个步骤(主要指MixedGC
):
初始标记(
InitialMark
):
- 先触发
STW
,然后使用单条GC线程快速标记GCRoots
直连的对象。并发标记(
ConcurrentMarking
):
- 与CMS的并发标记过程一致,采用多条GC线程与用户线程共同执行,根据
Root
根节点标记所有对象。最终标记(
Remark
):
- 同CMS的重新标记阶段(也会STW),主要是为了纠正并发标记阶段因用户操作导致的错标、误标、漏标对象。
筛选回收(
Cleanup
):
- 先对各个
Region
区的回收价值和成本进行排序,找出 回收价值最大 的Region
优先回收。- 筛选回收阶段在G1收集器中是会停止所有用户线程后,采用多线程并行回收的。
假设此时年老代空间共有
800
个Region
区,并且都满了,所以此刻会触发GC。但根据GC的预期停顿时间值,本次GC只能允许停顿
200ms
,而G1经过前面的成本计算后,大致可推断出:
- 本次GC回收
600
个Region
区恰好停顿时间可控制在200ms
左右,那么最终就会以 回收600
个Region
区 为基准触发GC,这样则能尽量确保GC导致的停顿时间可以被控制在我们指定的范围之内。G1会在后台维护着一个优先列表:
CollectionSet(CSet)
,它记录了GC要收集的Region
集合,集合里的Region
可以是任意年代的。在G1中回收算法都是采用复制算法,都会将一个
Region
区中存活的对象复制到另外一个Region
区内。
G1缺点:
G1需要记忆集 (卡表)来记录新生代和老年代之间的引用关系。
- 这种数据结构在 G1 中需要占用大量的内存,可能达到整个堆内存容量的 20% 甚至更多。
- 而且 G1 中维护记忆集的成本较高,带来了更高的执行负载,影响效率。
CMS 在小内存应用上的表现要优于 G1,而大内存应用上 G1 更有优势,大小内存的界限是6GB到8GB。