Java 8 默认GC是Parallel GC。设计初衷避免Full GC
一、Garbage First(G1)
适用服务器端、大内存、多CPU情景。
高效率回收(high thoughput)同时,提供软实时中断(所以不是最快,Parallel gc最快)。用户可指定时间上限,超过打断回收,恢复程序执行。
二、收集算法:标记-清理 ,拷贝-复制
https://www.jianshu.com/p/e37789a2916c另一版本
2.1适用G1案例:
需要大内存(堆6GB以上)、 小GC延迟(暂停时间0.5秒以内)
1、从CMS或者ParallelOld收集器切换到G1场景:
活动对象占50%以上Java堆空间
对象分配率或者提升率波动明显。
没有长时间垃圾收集暂停
2、G1特性:像CMS一样, 能与应用程序线程并行;整理空闲空间更快、预测gc停顿时间;不牺牲大量吞吐性能、不需更大Java Heap
3、G1目标是取代CMS,CMS相比,更出色:
1)G1有整理内存过程,不产生很多碎片。2)STW可控,添加预测机制,可指定https://mp.weixin.qq.com/s/4ufdCXCwO56WAJnzng_-ow
2.2 region:
heap分成若干大小相等region,每个占有一块连续虚拟内存,都关联Remembered set (RS)。
1、RS数据结构:hash table,数据是card table (heap中每512byte映射在card table 1byte)。RS里面存在region中live objects指针。
2、region数据变化:首先反映到card table中的一个或多个card上,RS通过扫描内部的card table得知region中内存使用情况和存活对象。
3、大对象H-objs(大于等于region一半)
1)直接分配到old gen,防止了反复拷贝移动。
2)在global concurrent marking阶段的cleanup 和 full GC阶段回收。
3)分配前检查是否超过 initiating heap occupancy percent和the marking threshold, 如超过启动global concurrent marking,提早回收,防止evacuation failures(疏散失败)和full GC。
4)减少连续H-objs分配对GC影响,把大对象变为普通的对象,建议增大Region size
4、Region大小可设定:-XX:G1HeapRegionSize,从1M到32M,且是2的指数。不设定,根据Heap大小自动决定。
2.3 关于内存分配:
G1主要关注于多CPU多线程,内存分配用 thread-local allocation buffers (TLABs)。线程都有自己buffers来分配对象,buffers不够,重新申请一块内存放自己thread-local里。对象内存分配被最小化到私有buffers里,缓解并发分配内存压力。
region满,分配内存线程会选择新region。空region在一个linked list里,快速找新
大对象(大小超过region的3/4)分配TLABs外。分配到特殊区域(只包含大对象)
2.4执行过程:
初始:STW(Stop the World ),所有mutator threads被停止,标记从GC Root开始直接可达对象,重启
并发:标记与应用程序线程并行,耗时长。
最终:标记上一阶段变化对象。需停顿,也要STW,并行很快完成。
筛选:对每区回收成本和价值排序,根据指定停顿时间,选择性收集,统计每区对象数。
三、G1与CMS不同:
1. 分代收集
CMS:只回收老年代(配合年轻代收集器);堆分PermGen,YoungGen(分了两个survivo),OldGen
G1中:回收老、年轻代,Fully young gc(先回收年轻代垃圾)和Mixed gc(年轻代和老年代,部分回收); 平均分,每个区也保留了新老代,区为单位收集
2. 如何处理跨代引用
老引用年轻代影响young gc,需跨代处理。
避免收年轻代时扫描老年代,记录老对年轻代引用,young gc时只要扫描这个记录。
CMS(基本用法):JVM将内存分成固定大小card,专门数据结构Card Table维护每个Card状态,一个字节对应一个Card(像内存page概念,page是硬件上,Card Table是软件上)。Card对象引用,对应Card Table上状态置为dirty,young gc扫描状态dirty的Card。
G1:Card Table基础上引入remembered set(RSet)。每个region维护一个RSet,记录着引用Card(其他region)。A对象在regionA,B在regionB,且B.f = A,regionA的RSet中记录B的Card地址。可对region单独回收,RSet维护老到轻、老引用,只扫描region的RSet的Card。
年轻(对象引用变化大)到老年代引用不需单独处理,性能提升很大,如都记成本高。只需在老年代维护Card Table。
3. 处理并发过程对象变化(未完成)
程序跟gc线程运行,新,旧的对象,引用关系变化。(每行表示一个内存状态,每列表示一个Card,4个):
a)并发标记a:中状态,标记a b c e四个对象,0 1两个Card已经标好
b)并发标记同时引用变:g不指d,b不再指c,指向d,这时处理Card 2,标记到g,标记结束,导致d对象丢失
(1)CMS初始标记:标记所有从root直接可达对象
并发标记:1)从这些对象进一步搜索其他可达对象,构成存活对象图。
2)Card Table记录引用变化。但young gc时如dirty card没包含到年轻代引用,card会重新标记为clean,可能将并发标记产生dirty card错误清除。
3)因此CMS引入mod union table,一个bit对应一个Card,young gc在将Card Table设置为clean的时候会将对应的mod union table置为dirty。最终标记的时候会将Card Table或者mod union table是dirty的Card也作为root去扫描,从而解决并发标记过程产生的引用变化。CMS还需要处理并发过程从年轻代晋升到老年代的对象,处理方式是将这部分对象也作为root去扫描。
(2)G1用snapshot at the beginning(SATB)算法
GC开始时活对象快照。通过Root Tracing得到,根据三色标记算法,维持并发GC正确性
初始标记时得到一个从root直接可达的snapshot,snapshot不可达对象都可gc,1)并发产生对象都默认活,留下一次处理。2)对引用变化,将对应Card放SATB队列里,最终标记时处理(如超阈值,并发标记也处理一部分,以队列中Card作为root进行扫描)
4. Write Barrier
写时插入一条特定操作
CMS:老引用年轻,通过触发Write Barrier更新Card Table标志位。同步操作,更新引用时顺带执行,引入的消耗不大(两个指令)。
G1复杂:两个地方用Write Barrier:
1.更新RSet的rememberd set Write Barrier:发生引用更新后,称Post Write Barrier
2. 记录引用变化的Concurrent Marking Write Barrier:发生引用变化前,称Pre Write Barrier。为提高性能,两个Write Barrier先放到队列中,异步处理
5. Full GC
CMS Full GC原因:Promotion Failure和Concurrent Mode Failure,晋升老年代没足够连续空间,可能内存碎片导致;jvm觉得并发结束前堆就满,提前触发Full GC。是多线程STW的Mark-Compact过程,需避免或者降低频率。
G1:Full GC对所有region做Evacuation-Compact(避免FullGC),单线程STW,耗时。原因(类似):1. Evacuation没有足够to-space放晋升对象;2. 完成前空间耗尽。
6.算法:
CMS标记—清理,G1复制,保证不产生多余的碎片。
7.G1停顿时间可控:
目的:减少STW时间,提高吞吐量
提供关键:
避免全域收集,有限时间内高效。G1跟踪各个Region里面的垃圾堆积的价值大小(回收获得空间大小及回收所需时间), 维护优先列表,价值最大Region先(Garbage-First来由)。
运作过程