? ? ? ? G1是一款既收集新生代又收集老年代的收集器,使用它可以实现整个java堆的gc,它有两个非常重要的新概念:region和remember set(简称rset)。
? ? ? ? region是啥?
? ? ? ? G1中虽然保留了新生代和老年代的概念,但是不像其他收集器那样使用物理内存隔离来区分新生代和老年代,在G1中新生代和老年代实现的是逻辑上的内存隔离,G1将整个的java堆最多分成2048个大小相等的region,每个region的大小为整堆实际大小/2048,在1-32M之间,并且为2的N次幂。对象是存储在region中的,有四种不同类型的region:E、S、O、H,分别对应着eden区的region、survivor区的region、老年代的region和h区的region,eden区的region用于存储新被创建的非巨型对象,survivor区的region用于存储从eden区复制过来的年龄还未达到15的存活对象,老年代的region用于存储从新生代晋升过来的老化对象,这些对象大多都是经过了多次gc仍然存活的对象,对象的存活概率非常大,h区用于存储巨型对象,那么什么是巨型对象呢?巨型对象是指体积庞大需要占用内存比较大的对象,在G1中对于巨型对象的定义是体积超过了一个region的容量的50%以上的对象。在其他的收集器实现中,巨型对象在被创建之后直接存放在老年代,在G1中,巨型对象在被创建之后是存放在h区的region中的,一个巨型对象会占用一个或多个连续的h区region。因为在G1中新生代和老年代实现的是逻辑内存的隔离,所以G1也不要求相同类型的region必须相邻。在G1中对象被保存在region中,所以在gc的时候,也是对region中的对象执行可达性分析,在可达性分析过程中,用到了一个新的概念:rset,G1收集器会用gc roots对象配合着rset记录来实现对象的可达性分析,那么什么是rset呢?
? ? ? ? rset是啥?
? ? ? ? rset是在G1收集器中使用的一个新的数据结构,它是一个列表,G1收集器会为每一个region创建一个rset,rset中记录的是其他老年代region中的对象对于此region中对象的引用,为啥要记录这些引用呢?我们知道,在其他的收集器中,当去执行对象的可达性分析时,需要沿着gc roots对象的引用链去查找对象,根据引用关系判断对象是否可达,而这些引用关系需要通过整堆扫描的方式才能获得,可整堆扫描耗时太长,为了在这一块儿进行优化,G1引入了rset去将引用关系记录下来,这样在可达性分析阶段就可以用扫描rset来代替整堆扫描了,大大提高了收集性能。
? ? ? ? G1的收集模式是怎样的呢?
? ? ? ? G1支持两种收集模式:young gc和mixed gc(混合收集)。young gc是对于新生代region的gc,混合gc是对于所有新生代region和部分老年代region的gc。
? ? ? ? 什么时候触发young gc呢?young gc并非是一遇到eden内存不足以存储新对象就立即触发的,初次遇到eden区的region不足以存储新对象时,G1首先做的是为eden区分配新的region,因为此时新生代的region占比仅为整堆内存的5%,G1允许继续为eden区分配新的region,但是新生代占比不能超过60%,当占比接近60%时,eden区的region内存不足就会触发young gc了。young gc在执行的时候,会利用gc roots对象和rset记录对新生代的region中的对象执行可达性分析,并且将存活对象拷贝到空闲的s区region中去,当然在这个过程中要完成存活对象年龄的增长,如果有哪个对象的年龄达到了15,那这个对象会被晋升到老年代的region中存储,如果eden区的region中有大量的对象存活了下来而没有足够的s区region去存储它们,也会有部分对象被直接存储到老年代region中。存活对象复制完毕后,将eden区的region以及只包含着垃圾的那些s区region清空。
? ? ? ? 随着程序的执行,不断有对象从新生代晋升到老年代中,老年代的region也慢慢被对象堆积起来,当老年代region占用达到了设置的整堆比阈值(默认45%)时,就要触发混合gc了,混合gc是对于全部的新生代region以及部分老年代region的收集,为什么是部分老年代而不是全部老年代呢?这是因为G1收集器另外一个新特性:支持可预测的STW时间设置,G1收集器支持我们设置在m毫秒时长内可用于STW的最大时长n的值,G1收集器需要在我们限定的这个时长内完成gc,所以它不能收集全部的region,只能收集部分region,那么收集哪一部分region呢?G1会对老年代所有region中的对象执行可达性分析,分析完成之后,所有region中的垃圾堆积情况就确定了,然后会对这些region按照回收价值和回收成本做一个优先级排序,有着更高的回收价值和更低的回收成本的region总会具有更高的优先级,而混合gc中被收集的也是具有最高优先级的那一部分region。
? ? ? ? G1收集器同CMS收集器一样,也追求更短的STW时间,所以它也是并发收集器的实现,它的收集过程也分为四个步骤:
? ? ? ? 1、初始标记:初始标记是去标记gc roots对象以及被gc roots对象直接关联的对象,这个过程要暂停用户线程,会有短暂的STW时间;
? ? ? ? 2、并发标记:并发标记是根据gc roots对象以及rset记录去标记其他对象,这个过程gc线程与用户线程并发,虽然耗时较长,但是用户线程不用暂停,没有STW时间;
? ? ? ? 3、重新/最终标记:因并发标记这一步,用户线程保持执行,那么难免在执行中发生对象引用关系的变化而造成多标和漏标的问题,因为漏标导致的结果很严重,会让不该被回收的对象被gc回收掉而造成程序错误,所以需要有重新标记过程。在并发标记过程中,如果有个白色对象被新引用了,这个引用会被记录下来,在重新标记阶段去为它标记颜色;即:重新标记是为了做标记修正。这一步需要暂停用户线程,会有STW时间,但是这一步耗时非常短;
? ? ? ? 4、筛选回收:按照优先级筛选部分region进行回收,这一步虽然会暂停用户线程,但是会有多个gc线程并行回收,STW时间很短。
? ? ? ? G1收集器的优点:
? ? ? ? 1、并行与并发:G1收集器充分利用多核CPU的优势,让用户线程与gc线程并发,缩短了STW时间,实现了更高的性能;
? ? ? ? 2、分代收集:G1收集器保留了分代的概念,既可以收集新生代,又可以收集老年代,无需其他收集器配合就能管理整个的java堆;
? ? ? ? 3、没有内存碎片问题:G1收集器从局部看用的是复制算法,从整体看用的是标记-整理算法,没有CMS收集器的内存碎片问题;
? ? ? ? 4、可预测的STW时间:G1收集器支持可预测的STW时间模型设置,让我们可以实现更好的用户线程暂停管理。
? ? ? ??