Garbage First
2004论文发布 ----> 2009 JDK 6u14 体验 ----> 2012 JDK 7u4 官方支持 ----> 2017 JDK9 默认
适用场景:
相关JVM参数:
-XX:+UseG1GC
-XX:G1HeapRegionSize=size
-XX:MaxGCPauseMillis=time

会STW
1)、将内存换分为多个区域,E代表Eden

2)、当Eden区域逐渐被占满,触发 Minor GC,采用复制算法,将幸存的对象复制到幸存区。S:代表幸存区。

3)、在经过一段时间之后,幸存区逐渐被占满,那么将年龄够的对象晋升到老年代。将不符合年龄要求的对象,拷贝到新的幸存区。

在Young GC时会进行GC Root的初始标记(找到根对象,STW时进行,在上一个阶段执行)
老年代占用堆空间比例达到阈值时,会进行并发标记(根据根对象的引用链,找到其他对象,不会STW),由下面的JVM参数决定
-XX:InitiatingHeapOccupancyPercent=percent ( 默认45% )

会对 E、S、O 进行全面的垃圾回收
最终标记(Remark)会STW,因为并发标记可能有遗漏
拷贝存活(Evacuation)会STW
-XX:MaxGCPauseMillis=ms (最大暂停时间)
对于新生代,将Eden区的幸存对象拷贝到幸存区,将其他幸存区里面的对象,该晋升的晋升,该继续保留的保留。其他的就全部进行回收了。
对于老年代,因为老年代会很大,默认45%,那么根据我们最大暂停时间,可能在这设置的时间内,我们不能讲全部老年区进行判断回收,那么系统就会判断,那么回收等级高,就优先回收哪些优先等级高的。


CMS,在碎片少的时候,是并发执行垃圾回收的,如果要是并发失败,会退化为 Serial GC,那么这种产生的GC 才叫Full GC
G1 ,当你垃圾产生的速度 小于 垃圾回收的速度,这不叫Full GC。 当你垃圾产生的速度 大于 垃圾回收的速度,才会产生 Full GC,并发回收失败,进行并行回收。
新生代回收的跨代引用(老年代引用新生代)问题
当新生代对象被老年代对象引用,我们将老年代再进行细分,每个大约512k大小。使用卡表技术
如果这个老年代引用了新生代对象,那么这个卡,我们就标记为脏卡。以后我们就不用遍历整个老年代,减少搜索范围,提高GC Root的寻找效率。
新生代会记录脏卡,那么在 新生代垃圾回收时,就会根据Eden记录脏卡区域进行遍历查询GC Root

在并发标记过程中,产生了新的引用,原本应该被回收的对象被引用了。但是并发标记并没有对 被引用的对象进行重新标记。
所以重新标记就是为了解决这种情况。
具体做法,在并发标记时:当对象引用 发生改变时,JVM就会对该对象加入一个 写屏障 (只要对象引用发生了改变,就会触发写屏障)。
写屏障:会将该对象加入一个队列(satb_mark_queue队列)之中,将该对象变为正在处理中的状态,进入重新标记阶段。
重新标记,会STW,将队列中的 对象取出来,进行再一次的判断出来,判断队列中的对象是回收还是保留。
优点:节省大量内存
去电:略微多占用cpu时间,新生代回收时间略微增加
-XX:+UserStringDeduplication
jdk8 中字符串的存储底层实际采用的char数组进行存储,new两个,那么也是存两个char数组
String s1 = new String("hello"); // char[]{'h','e','l','l','o'}
String s2 = new String("hello"); // char[]{'h','e','l','l','o'}
所有对象都经过并发标记后,就能知道哪些类不再被使用,当一个类加载器的所有类都不再被使用,则卸载它所加载过的所有类。
例如,很多框架都是用自定义加载器。
-XX:+ClassUnloadWithConcurrentMark // 默认启用
这样可以尽可能避免 G1 GC 退化为 FullGC