CMS 和 G1 算法
CMS GC (-XX:+UseConcMarkSweepGC)
并发标记扫描(Concurrent Mark Sweep,CMS)专为需要较短暂停的应用而设计的老年代收集器,在应用程序运行时需要与GC线程共享处理器资源,所以需要运行在多核处理器上。通常,在具有较多长生命周期数据集(老年代比较大)的应用程序上使用,从JDK 9 开始不推荐使用CMS收集器,推荐使用Garbage-First收集器
流程
每次major collection
,CMS收集器会在收集开始时暂停所有应用程序线程,叫做initial mark pause,在并发跟踪阶段结束后会再次暂停,叫做remark pause,第二次暂停往往时间较长,通常与minor collection
相当,两个暂停期间都是多个线程执行收集工作
- 初始标记 Initial Mark :Stop-The-World,仅仅标记GC Roots直接关联的对象,例如线程栈和寄存器1中引用的对象,静态对象等,速度很快
- 并发标记 Concurrent Mark:该阶段中不需要暂停应用程序,进行标记
- 重新标记 Remark:再次暂停应用程序,修正因为并发标记阶段应用线程运行导致的对象更新,确保所有的可达对象都被标记,时间稍长
- 并发清除 Concurrent Sweep:执行sweep,来清除所有非可达对象所占用的内存空间,存活的对象没有移动,除非出现
Concurrent Mode Failure
,触发一次full GC,进行整理 - 重置 Resetting:清除数据结构,下一次并发收集做准备
Serial GC 和 CMS GC
由于应用程序线程和垃圾收集器线程在major collection
期间并发运行,因此垃圾收集器线程跟踪的存活对象可能再收集过程结束时变得不可访问。这些尚未回收的不可达对象被称为浮动垃圾floating garbage。浮动垃圾的数量取决于并发收集周期的持续时间以及应用程序的引用更新频率(也称为突变,mutation)。在下一次GC期间收集在当前并发收集周期结束时堆中的浮动垃圾
Concurrent Mode Failure
CMS收集器使用一个或多个与应用程序线程同时运行的垃圾收集器线程,目标是在老年代变满之前完成收集
在正常操作中,CMS收集器在应用程序线程仍在运行时执行大部分的跟踪和清除工作,因此应用程序线程只会有短暂的暂停。但是,如果CMS收集器无法在老年代填满之前完成垃圾回收,或者老年代中的可用空闲块不能满足分配需求,则所有应用程序都将暂停,等待收集完成。无法并发完成收集的情况称为并发模式失败,表示需要调整CMS收集器参数
启动时机
串行收集器只要老年代变满就进行major collection
,在收集过程中停止所有应用线程
CMS收集器中并发收集必须定时开始,以便GC可以在老年代填满之前完成,否则会产生并发模式故障Concurrent Mode Failure,导致更长的暂停,CMS内部会根据历史记录,对老年代内存被填满的剩余时间以及并发的收集时间进行估计,确保在老年代用尽之前完成GC,避免并发模式故障
由于要预留空间给浮动垃圾,如果老年代的内存占用率超过CMS初始化占用比例,同样也会触发CMS,调整触发CMS的老年代百分比-XX:CMSInitiatingOccupancyFraction=<N>
缺点
- 占用更多的CPU和内存资源
- 无法处理Floating Garbage,需要预留一定的空间
- 标记-清除算法,不进行copy或者compact,会产生碎片,连续空间不够分配,导致
concurrent mode failure
G1 (XX:+UseG1GC)
Garbage-First(G1) 垃圾收集器适用于具有大量内存的多处理器计算机,它在延迟和吞吐量之间寻找一个最佳平衡,JDK 9开始作为默认GC
特点:
- 堆大小为10GB或更大,超过50%的Java堆被实时数据占用
- 对象分配和晋升的比率可能会随着时间的推移显着变化
- 堆中存在大量碎片
- 可预测的暂停时间,不超过几百毫秒,避免长时间的垃圾收集暂停
G1是一个分代收集收集器,将堆分成新生代和老年代,算法是并行的,大部分阶段是并发运行,部分操作会产生STW暂停,空间回收是逐步增加的,主要通过疏散的方式进行。G1通过跟踪先前应用程序行为和垃圾收集暂停的信息来建立相关成本的模型,从而实现可预测性,通过模型确定暂停时间内要完成的工作量,首先在最有价值的区域中回收空间(即区域中大部分都是垃圾,这也是G1名称的由来)
G1主要通过疏散evacuation回收空间:在选定区域内的活动对象会被复制到新的内存区域,同时在此过程中进行整理将对象放在一起,疏散完成后,先前占用的空间就可以回收利用
G1收集器有很高的概率,满足设定的GC停顿时间,但是并不绝对完成,所以G1不是一个实时的收集器(real-time collector)
堆布局
G1将堆分区为一组大小相等的区域,每个区域都是一个连续的虚拟内存区域,区域是内存分配和内存回收的单位,在某一时间,这些区域可以是空的,或者分配给了年轻代或老年代
区域的大小通过-XX:G1HeapRegionSize
设定,默认区域大小基于堆的初始和最大大小,堆包含大约2048个区域。堆区域的大小可以在1到32 MB之间变化,但必须是2的幂次
G1堆分配这里貌似限定了堆空间的最大值就是64GB?
Humongous Objects
巨型对象是大于或等于半个区域大小的对象
- 巨型对象都分配在一系列连续区域上,作为老年代。序列的最后一个区域中的剩余空间都不会进行分配
- 巨型对象的分配可能导致垃圾收集暂停过早发生,每次巨型对象分配时G1都会检查初始堆占用阈值,并且如果当前占用率超过该阈值,则立即开始新生代收集初始标记
- 即使在Full GC期间,这些巨型对象也不会移动
垃圾收集周期
G1收集器在两个阶段之间交替:
- young-only phase:期初是进行正常的年轻代收集
Normal young collections
,晋升对象到老年代,当老年代的占用率到达堆占用阈值,进行并发收集Concurrent Start young collection
- 并发启动
Concurrent Start
:在执行Normal young collections
的STW时进程Initial mark
,然后启动并发标记Concurrent marking
,并发标记确定老年代区域中当前可到达(存活)的所有对象,以便空间清理阶段保留。收集标记进行过程中,也会出现正常的代收集,即可以被年轻代的STW中断。标记完成还有两个停顿:重新标记和清理 - 重新标记
Remark
:通过STW最终确定标记,执行全局引用处理和类卸载,回收完全空白区域并清理内部数据结构。在重新标记和清理之间G1计算信息以便以后能够并发回收所选老年代区域中的可用空间 - 清理:这是标记的最后阶段,STW决定是否执行
space-reclamation
- 并发启动
- space-reclamation phase:空间回收阶段由多个混合收集
Mixed collections
组成,除了处理新生代之外,老年代的空间也会进行疏散,当G1确定疏散更多的老年代区域产生的自由空间不值得努力时,空间回收阶段结束
如果应用程序在垃圾回收时内存不足,则G1会像其他收集器一样STW,执行Full GC
Collection Set:将要被GC收集的region集合,内部的存活对象在GC期间都会被疏散(复制/移动)
在young-only
阶段,收集的区域集合(Collection Sets)仅由新生代区域组成,G1每次在年轻代收集的末尾计算新生代的大小,在下一个阶段改变。如果没有另外约束,则G1自适应地在-XX:G1NewSizePercent
和-XX:G1MaxNewSizePercent
之间调整大小来满足暂停时间的要求
在space-reclamation
阶段,G1试图最大化回收老年代的内存,按照回收的效率高低清理老年代区域,同时通过剩余可用时间确定最终收集区域的大小,活动对象比例在-XX:G1MixedGCLiveThresholdPercent
阈值之上的不进行清理,-XX:G1MixedGCCountTarget
设定该阶段中进行Mixed垃圾收集的次数
当收集集候选区域(collection set candidate regions)中可回收的空间量小于-XX:G1HeapWastePercent
设置的百分比时,结束该阶段
确定启动堆占用率
启动堆占用百分比(Initiating Heap Occupancy Percent, IHOP)是触发初始标收集的阈值,定义为老年代大小的百分比
G1默认情况下通过观察标记所花费的时间以及在标记周期内老年代分配的内存数量来自动确定最佳的IHOP,此功能称为自适应IHOP。如果此功能处于活动状态,-XX:InitiatingHeapOccupancyPercent
只有在没有足够的观察值来进行预测时使用,选项-XX:-G1UseAdaptiveIHOP
可以关闭G1的自适应调整,此时一直通过-XX:InitiatingHeapOccupancyPercent
确定阈值
标记
G1标记使用Snapshot-At-The-Beginning (SATB) 算法。 在Initial Mark
暂停时获取堆的虚拟快照,在标记开始时存活的所有对象在后来的标记过程中都认为是存活对象。这意味着在标记期间变为死亡(无法访问)的对象在空间回收阶段仍然被认为存活,进行疏散 ,与其他收集器相比,这可能会导致错误地额外保留一些内存。但是,SATB可以降低Remark阶段的延迟
网友评论