G1收集器是一款在server端运行的垃圾收集器,专门针对于拥有多核处理器和大内存的机器。
特点
- 设置一个垃圾的预期停顿时间.根据Region的大小和回收价值进行最有效率的回收
- 内存不再固定划分新生代和老年代,使用Region对于内存进行分块,实现了根据系统资源动态分代
- Region可能属于新生代或者老年代,同时分配给新生代还是老年代是由G1自己控制的
- 选择最小回收时间以及最多回收对象的Region进行垃圾回收操作.
与CMS相比G1的优势
- G1是一个有整理内存过程的垃圾收集器,不会产生很多的内存碎片
- G1的STW更可控
- G1在停顿时间上加了预测机制,用户可以指定期望的停顿时间
G1适合什么系统
G1适合的系统包括超大内存的系统,此时如果按照传统的分代,比如16G内存新生代8G,老年代8G这种划分,虽然拉长垃圾停顿时间,但是一旦新生代被占满,回收的效率是非常低的,因为对象和GC ROOT非常多,最终导致垃圾收集长时间卡顿.而G1不一样,他只需要按照算法判断根据停顿模型的值在新生代接近停顿模式时间的时候就会马上开启回收,不用等新生代满了才回收
G1也适合需要低延迟的系统,因为延迟低对于系统的响应要求是非常高的,更加看重响应时间,同时对于系统资源的要求也比较高,而分代的模型和理论在大内存机器回造成长时间的垃圾收集停顿,这对于实时响应的服务要求是非常高的
G1种的几个重要概念
Region
传统的GC收集器将连续的空间划分为新生代 老年代和元空间,这种划分的特点是各代的存储地址(逻辑地址)是连续的
而G1的各代存储地址是不连续的,每一代都使用了n个不连续的大小相同的Region,每个Region占有一块连续的虚拟内存地址
STAB
在global concurrent marking种使用到了该算法,全称snapshot-at-the-beginning,其目的是为维护并发GC的正确性;GC正确性是保证存活对象不被回收,一言以蔽之就是保证回收的都是垃圾
STAB工作原理
为了解决上面的保证GC正确性,STAB的做法是在GC开始时对内存进行一个对象的逻辑快照(snapshot),通过GC Roots tracing 参照并发标记的过程,只要被快照到的对象都是活的,那在整个GC过程中对象都被认为是活的,即使该对象的引用稍后被修改过或删除.同时新分配的对象也被认为是活的,除此之外其它不可达的对象就被认为是死掉了.这样STAB就保证了真正存活的对象不会被GC误回收,但是同时也会导致某些垃圾没有被GC,导致了所谓的浮动垃圾的产生
G1垃圾回收步骤
初始标记
和CMS垃圾收集器类似,在初始的情况需要STW,仅仅标记GC ROOT可以引用的对象,整个过程非常的快
这里可以看到空间的结构以及和传统固定的分代模型已经不一样了,堆内存欸划分为小块的region,初始标记会根据栈中的局部变量引用或者传递引用等标记初始的GC ROOT对象,这个过程可以非常快,因为仅仅是简单标记
并发标记
这个阶段也和CMS比较类似,同样可以和用户线程一起并发执行,系统可以正常分配对象,而垃圾回收线程根据GC ROOT进行对象引用,标记存活对象.同时将对象的改动进行标记记录,这个阶段会比较耗费系统资源,但是和系统线程一起并发的.
最终标记
这个阶段和CMS也还是很类似的,需要STW,此时系统线程需要暂停,停止对象的分配,同时垃圾收集器负责对于对象进行最后的标记和分类动作,决定哪些对象需要被垃圾回收
筛选回收
筛选回收阶段是G1种最重要的一个阶段.这个阶段会计算老年代有多少个Region存活,存活对象的占比,以及回收的效率计算.
这个阶段也会STW,会让垃圾线收集线程马力全开,在指定的停顿时间范围完成更多的垃圾回收动作.同时这个阶段会重复很多次,并且在回收的时候和系统线程是交替运行的,也就是说回收的时候需要暂停
另外回收阶段不仅回收老年代,还回收新生代以及大对象,算是真正意义上的full GC.比如:老年代有1000个region,而通过计算需要回收的region为800个,那么就会回收800个Region
系统线程和垃圾回收是交替运行的,在最后一步默认会让系统和垃圾收集线程交互运行8次,假设停顿时间设置为200ms,那么就是每次在200ms内尽可能回收垃圾,回收完成立马开启系统线程,然后运行一段时间又进行回收,如此反复,如果垃圾回收中4次就回收掉了5%,那么意味着这一阶段提前完成了,就会立马进入下一次的垃圾回收循环
对应回收参数:
-XX:G1MixedGCCountTarget: 表示一次垃圾回收之后,最后一次垃圾回收执行几次.这个参数意味着在垃圾回收的最后一阶段回收和系统运行交替运行,并且回收8次之后结束这个操作-XX:G1HeapWastePercent:默认为5%.在混合回收的时候如果一次混合回收的空闲region超过5%就立刻停止垃圾回收操作,这个参数也是为了尽量减少停顿时间设计的
G1的FAQ
下面的一下问答,其实涵盖了大部分学习G1的所遇到的问题,可以通过下面的一下QA加上自我验证,巩固对G1垃圾收集器的认知
G1在什么时候会出发Mixed GC
参数-XX:InterfaceTestControllernitiatingHeapOccupancyPercent,他的默认值是45%。意思就是说如果老年代占用超过了45%,就会出发一个叫做混合回收的操作,混合回收意味着新生代和老年代一起回收,这时候毫无疑问整个系统线程都会停止
回收失败了如何处理
回收失败了,就会停止线程,然后通过单线程的serrial的方式对所有的region存活对象标记,整理,然后清理垃圾对象,整个过程是非常缓慢的.
这个过程其实和CMS的Counrrent mode fail类似,但是由于G1的内存模型完全另辟蹊径,所以他需要回收新生代,老年大和大对象,算法的细节更要复杂,整理恢复的时间也比较长,也可以说G1的回收是真正意义上的full GC
G1在存在eden区域和survior区域吗
虽然不需要指定新生代和老年代的大小由G1控制,但是region本身在运行的时还是会划分为新生代或者老年代,只是不再是固定的了,所欲新生代还是有对应得eden和Survior区域
G1在新生代是如何回收的?
采用复制算法,当新生代超过默认得60%得最大占比限制得时候,会触发Minor GC,然后进行 stop world得操作.这里可能会想这不和之前的没什么区别么?之前说过G1的特点是指定垃圾回收的最大停顿时间,G1会根据Region的大小和回收预测时间再我们指定的最大回收时间尽可能的回收内存,回收之后的内存可以给新生代使用也可以给老年代使用.
G1在老年代是如何回收的?
G1本身已经没有老年代回收这个概念了,取而代之的是Mixed GC也就是混合回收,当老年代region占比超过45%就会触发
G1的回收和之前的分代垃圾收集器有什么区别?
Region虽然是分代存储的但是并不代表一直是分代的region,比如新生代如果有600和region,回收了200个region,这个200region等于是自由的,可以分配给新生代也可以分配给老年代
G1停顿时间模型会根据用户设置的比如200MS内进行回收操作,可以通过-XX:MaxGCPauseMills这个参数设置最大的停顿等待时间,G1会根据此参数追踪最有回收价值的region进行处理,但是这个参数其实是一个软目标,并不是说收集器完成保证这个时间段内完成回收,而是意味着这个时间段内做最有价值的回收.
对象什么时候进入老年代
- 对象在新生代躲过了很多次垃圾回收,达到一定对象年龄,
-XX:MaxTenuringThreashold这个参数可以设置年龄。 - 根据Survior总体存活率超过50%的情况判断,当排序发现某一年龄对象大小超过survior区域的50%会触发
大对象什么时候回收
大对象不属于新生代也不属于老年代,所以在新生代或者老年代回收的时候会顺带回收大对象的region内存,也就是说大对象的回收依靠Mixed回收.