企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
### G1垃圾回收的过程 **dirty card queue:对于应用程序的引用赋值,如object1.field = object2,JVM会在之前和之后执行特殊的操作以在dirty card queue中入队一个保存了对象引用信息的card,在年轻代回收的时候,G1会对dirty card queue中所有card进行处理,以更新RSet,保证RSet实时准确的反应引用关系。****为什么不在赋值的时候直接更新RSet呢?这是为了不损失性能,RSet的处理需要线程同步,开销大,使用队列可以一定程度上缓解同步操作RSet带来的性能损失。** * 年轻代GC * **并行的、独占式的(STW发生)的垃圾回收**,可能会发生对象的代晋升,将会把对象放入Survivor或者是老年代。 * 当Eden区内存空间耗尽的时候,G1会启动一次年轻代垃圾回收,顺带回收Survivor区。 * G1 Young GC的时候,首先停止程序线程(STW),G1创建回收集(Collection set,需要被回收的内存分段集合,包括Eden区和Survivor区的所有内存分段) * 第一阶段:扫描根(GC Roots),GC Roots联通RSet记录的外部引用作为扫描存活对象的入口。 * 第二阶段:处理**dirty card queue**中的card,更新RSet。此阶段完成后,**RSet可以准确的反应老年代对所在的内存分段中对象的引用**。 * 第三阶段:识别被老年代对象指向的Eden区中的对象,这些被指向的Eden中对象被认为是存活的对象。 * 第四阶段:复制对象(**使用的是复制算法**),对象数被遍历,Eden区region中存活的对象会被复制到Survivor区中的region,Survivor region中的存活对象如果年龄未达到阈值,年龄+1,达到阈值会被复制到Old region,如果Survivior的空间不够用,Eden中的部分数据会直接晋升到老年代。 * 第五阶段:处理Soft、Weak、Phantom、Final、JNI Weak等引用,最终Eden区的数据为空,这些空的region将会等待对象的分配,GC停止工作,目标内存中的对象也都是连续的,没有内存碎片。 * 老年代并发标记 * **当堆空间的内存占用达到阈值(-XX:InitiatingHeapOccupancyPercent,默认45%)就开始老年代的并发标记过程**。 * 初始标记阶段:**标记GC Roots直接可达的对象,也就是直接引用关系对象**,会发生STW(由于是直接可达的对象的标记,所以暂停时间很短),**并且会触发一次Young GC** * 根区域的扫描(Root Region Scanning):G1扫描Survivor区直接可达的老年代区域对象,并标记被引用的对象。这一个过程**必须在Young GC之前完成(因为Young GC会操作Survivor区中的对象)**。 * 并发标记(Concurrent Marking):在整个堆中进行并发标记(与程序线程并发执行),**此过程可能会被Young GC打断**,在并发标记阶段中,**若发现某些region中所有对象都是垃圾,那这个region就会被立即回收**,同时并发标记过程中,会**计算每个region的对象活性(该region存活对象的比例,G1垃圾回收的时候并不是所有region都会参与回收的,根据回收的价值高低来优先回收价值较高的region)**。 * 再次标记:由于并发标记阶段是收集器的标记线程和程序线程并发执行的,需要进行再次标记,修正上一次的标记结果,可以理解为增量补偿标记。会出现STW(暂停时间较短)。G1采用的是比CMS跟快的初始快照算法:snapshot-at-the-beginning(SATB) * 独占清理:计算各个region的存活对象和GC回收比例,并进行排序(回收价值高低排序),识别可以混合回收的区域。为下阶段做铺垫,会发生STW。**需要注意的是这个阶段实际上并不会做垃圾的回**收。 * 并发清理阶段:识别并清理完成空闲的区域 * 混合回收 ![](https://img.kancloud.cn/34/59/3459872f815223e9d48ac36d2ca540d5_584x475.png) * 包括年轻代和老年代的垃圾回收 * 标记完成后马上开始垃圾的回收。对于一个混合的回收过程,G1从老年代移动存活的对象到空闲区域,这些空闲的区域变成了老年代region。当越来越多的对象晋升到老年代region的时候,为了避免堆内存被耗尽,就会触发以哦个混合垃圾收集Mixed GC,该算法并不是一个Old GC也不是Full GC,除了回收整个Young region之外,还会回收一部分Old region,**部分的region垃圾回收设计可以对垃圾回收的耗时进行控制**。 * 在并发标记结束之后,老年代中能够完全确认为垃圾的region中的内存分段被回收了,部分为垃圾的region中内存分段也被计算出来了,默认情况下,这些老年代的内存分段会被分为8次回收(**可以通过-XX:G1MixedGCCountTarget设置**)。 * 混合回收的回收集包括1/8的老年代的内存分段,Eden区内存分段,Survivor内存分段,**混合回收的算法和年轻代回收的算法完全一致**。 * 混合回收并不一定要进行8次,有一个**阈值设置:-XX:G1HeapWastePercent,默认值10%,代表允许整个堆内存中有10%的内存可以被浪费,意味着如果发现可以回收的垃圾占对内存的比例低于10%,则不进行混合回收**,因为GC花费的时间相对于较少的垃圾回收来说得不偿失。 * **由于老年代的内存分段默认分为8次回收,G1会优先回收垃圾多的内存分段,垃圾占内存分段比例越高的会优先被回收**。并且有**一个阈值决定内存分段是否被回收:-XX:G1MixedGCLiveThresholdPercent,默认为65%,代表垃圾占内存分段比例要达到65%来回被回收**,如果垃圾占比太低,意味着存活的对象多,复制算法就会花费更多的时间区复制存活的对象。 * 必要的情况下(对象分配速度远大于回收速度),Full GC仍然会触发(Full GC的成本较高,单线程,性能差,STW时间长) * 堆内存太小、对象分配速度远大于回收速度等原因都可以导致G1在复制存活对象的时候没有空闲的内存分段可用,最终造成Full GC的触发。