[{"data":1,"prerenderedAt":206},["ShallowReactive",2],{"blog:\u002Fblog\u002F2021\u002F10\u002F04\u002Fgc-g1-process\u002F":3},{"id":4,"title":5,"body":6,"categories":192,"comments":194,"date":195,"description":196,"draft":197,"extension":198,"legacySlug":80,"meta":199,"navigation":194,"path":200,"seo":201,"stem":202,"tags":203,"updated":204,"__hash__":205},"blog\u002Fblog\u002F2021\u002F10\u002F04\u002Fgc-g1-process.md","JVM：G1 GC流程",{"type":7,"value":8,"toc":179},"minimark",[9,14,18,21,25,33,36,39,43,48,51,55,65,69,72,76,83,87,98,102,107,111,118,122,129,133,137,151,155,169,172,176],[10,11,13],"h1",{"id":12},"_1简介","1、简介",[15,16,17],"p",{},"G1全称Garbage First，是垃圾收集器发展历史上里程碑式的成果，它开创了收集器面向局部收集的设计思路和基于Region的内存布局形式。",[15,19,20],{},"作为CMS收集器的替代者，设计者希望做出一款支持\"停顿预测模型\"(Pause Predictioin Model)的收集器。在G1收集器出现之前，包括CMS在内，垃圾收集的目标要么是整个新生代（Minor GC)，要么就是整个老年代（Major GC)，要么就是整个Java堆（Full GC)。而G1跳出了这个樊笼，它可以面向堆内存任何部分来组成回收集（Collection Set）进行回收，衡量标准不再是它属于哪个分代，而是哪块内存中存发的垃圾数量最多，回收收益最大，这就是G1收集器的Mixed GC模式。",[10,22,24],{"id":23},"_2g1堆内存布局","2、G1堆内存布局",[15,26,27,32],{},[28,29],"img",{"alt":30,"src":31},"g1_heap","\u002Fblog-assets\u002Fgc-g1-process\u002Fgc-g1-heap.png","\nG1不再坚持固定大小以及固定数量的分代区域划分，而是把连续的java堆划分为多个大小相等的独立区域（Region)，每一个Region都可以根据需要，扮演新生代的Eden空间、Survivor空间或者老年代空间。收集器能够对扮演不同角色的Region采用不同的策略去处理，这样无论是新创建的对象还是已经存活了一段时间、熬过多次收集的旧对象都能获取很好的收集效果。",[15,34,35],{},"上图中的H区（Humongous）专门用来存储大对象。G1认为只要大小超过一个Region容量的一半就可判定为大对象。每个Region的大小可以通过参数-XX:G1HeapRegionSize设定，取值范围为1MB~32MB，且应为2的N次幂。而对于超过了整个Region容量的超级大对象，将会被存放在N个连续的H区中，G1的大多数行为把H区作为老年代的一部分来看待。",[15,37,38],{},"“G1收集器之所以能建立可预测的停顿时间模型，是因为它可以有计划地避免在整个Java堆中进行全区域的垃圾收集。G1跟踪各个Region里面的垃圾堆积的价值大小（回收所获得的空间大小以及回收所需时间的经验值），在后台维护一个优先列表，每次根据允许的收集停顿时间（使用参数-XX：MaxGCPauseMillis指定，默认为200ms），优先回收价值最大的Region（这也就是Garbage-First名称的来由）。这种使用Region划分内存空间以及有优先级的区域回收方式，保证了G1收集器在有限的时间内可以获取尽可能高的收集效率。”",[10,40,42],{"id":41},"_3实现难点","3、实现难点",[44,45,47],"h2",{"id":46},"_31如何解决跨代引用","3.1、如何解决跨代引用",[15,49,50],{},"G1收集器同样使用记忆集来解决跨代引用问题，但在G1收集器上记忆集的应用其实要复杂的多，它的每个Region都需要维护自己的记忆集。因此G1收集器比其他的传统垃圾收集器有着更高的内存占用负担。根据经验，G1至少要耗费Java堆容量的10%至20%的额外内存来维持收集器工作。",[44,52,54],{"id":53},"_32如何并发标记","3.2、如何并发标记",[56,57,58,62],"ul",{},[59,60,61],"li",{},"G1使用原始快照（SATB）方式解决三色标记法可能出现的错标问题",[59,63,64],{},"G1为每一个Region设计了两个名为TAMS（Top at Mark Start）的指针，把Region中的一部分空间划分出来用于并发回收过程的新对象分配，并发回收时新分配的对象地址都必须要在这两个指针位置之上。G1收集器默认在这个地址以上的对象是被隐式标记过的，即默认它们是存活的，不纳入回收范围。",[44,66,68],{"id":67},"_33如何建立可靠的停顿预测模型","3.3、如何建立可靠的停顿预测模型",[15,70,71],{},"G1收集器的停顿预测模型是以衰减均值（Decaying Average）为理论基础来实现的。在垃圾收集过程中，G1收集器会记录每个Region的回收耗时、每个Region记忆集里的脏卡数量等各个可测量的步骤花费的成本，并分析得出平均值、标准偏差、置信度等统计信息。",[10,73,75],{"id":74},"_4收集过程","4、收集过程",[15,77,78,82],{},[28,79],{"alt":80,"src":81},"gc-g1-process","\u002Fblog-assets\u002Fgc-g1-process\u002Fgc-g1-process.png","\nG1收集器的运作过程大致分为以下四个步骤：",[44,84,86],{"id":85},"_41初始标记","4.1、初始标记",[56,88,89,92,95],{},[59,90,91],{},"标记GC Roots能直接关联到的对象",[59,93,94],{},"修改TAMS的值，让下一阶段用户线程并发运行时，能正确地在可用的Region中分配新内存。",[59,96,97],{},"STOP THE WORLD",[44,99,101],{"id":100},"_42并发标记","4.2、并发标记",[56,103,104],{},[59,105,106],{},"从GC Root开始对堆中对象进行可达性分析，耗时较长，但可与用户线程并发执行",[44,108,110],{"id":109},"_43最终标记","4.3、最终标记",[56,112,113,116],{},[59,114,115],{},"处理并发标记期间发生变动的对象引用（原始快照法）",[59,117,97],{},[44,119,121],{"id":120},"_44筛选回收","4.4、筛选回收",[56,123,124,127],{},[59,125,126],{},"更新Region的统计数据，对各个Region的回收价值和成本进行排序，根据用户期望的停顿时间制定回收计划。可自由选择任意多个Region构成回收集，把要回收的那一部分Region的存活对象赋值到空的Region中，再清理掉整个旧Region的全部空间。",[59,128,97],{},[10,130,132],{"id":131},"_5与cms对比","5、与CMS对比",[44,134,136],{"id":135},"_51优点","5.1、优点",[56,138,139,142,145,148],{},[59,140,141],{},"可指定最大停顿时间",[59,143,144],{},"分Region的内存布局",[59,146,147],{},"按受益动态确定回收集",[59,149,150],{},"不会产生内存碎片",[44,152,154],{"id":153},"_52缺点","5.2、缺点",[56,156,157,166],{},[59,158,159,160,165],{},"内存占用\nG1的卡表实现更为复杂，而且堆中每个Region，无论扮演的是新生代还是老年代角色，都必须有一份卡表，这导致G1的记忆集（和其它内存消耗）可能会占用整个堆内存的20%或者更多。\n相比而言，CMS的卡表就相当简单，只有唯一一份，而且只用处理老年代到新生代的引用，反过来则不需要，由于新生代的对象具有朝生夕死的不稳定性，引用变化频繁，能省下这个区域的维护开销是很划算的（",[161,162,164],"span",{"color":163},"red","代价是当CMS发生Old GC时，要把整个新生代作为GC Roots来进行扫描","）。",[59,167,168],{},"CPU负载\nG1对写屏障的复杂操作要比CMS消耗更多的运算资源。所以CMS的写屏障实现是直接的同步操作，而G1就不得不将其实现为类似于消息队列的结构，把写前屏障和写后屏障要做的事情都放到队列里，然后再异步处理。",[15,170,171],{},"目前在小内存应用上CMS的表现大概率会优于G1，而在大内存应用上G1则大多能发挥其优势，这个优劣势的java堆平衡点通常在6GB至8GB之间。",[10,173,175],{"id":174},"_6参考","6、参考",[15,177,178],{},"1、《深入理解JAVA虚拟机》",{"title":180,"searchDepth":181,"depth":181,"links":182},"",2,[183,184,185,186,187,188,189,190,191],{"id":46,"depth":181,"text":47},{"id":53,"depth":181,"text":54},{"id":67,"depth":181,"text":68},{"id":85,"depth":181,"text":86},{"id":100,"depth":181,"text":101},{"id":109,"depth":181,"text":110},{"id":120,"depth":181,"text":121},{"id":135,"depth":181,"text":136},{"id":153,"depth":181,"text":154},[193],"JVM",true,"2021-10-04","1、简介 G1全称Garbage First，是垃圾收集器发展历史上里程碑式的成果，它开创了收集器面向局部收集的设计思路和基于Region的内存布局形式。 作为CMS收集器的替代者，设计者希望做出一款支持\"停顿预测模型\"(Pause Predictioin Model)的收集器。在G1收集器出现之前",false,"md",{},"\u002Fblog\u002F2021\u002F10\u002F04\u002Fgc-g1-process",{"title":5,"description":196},"blog\u002F2021\u002F10\u002F04\u002Fgc-g1-process",[193],null,"rKygqPZfpMyksxBJsb5PtzG6WQutBT0Ufn34i2kCgZQ",1783807996344]