serial收集器:
最古老的收集器,单线程,收集工作的时候需要暂停其他所有的工作线程(stop the world)。老年代版本:serial old
新生代复制,老年代标记-整理
parallel scavenge:
serial多线程版本。 关注点是吞吐量
老年代版本:parallel old
新生代复制,老年代标记整理
ParNew收集器:
serial多线程版本,主要为了减少stw,不是提升吞吐量(cpu利用率)
4核CPU就可以支持4个垃圾回收线程并行执行,可以提升4倍的性能
通常和CMS(current mark sweep)配合使用
老年代的对象存活率一般是比较高的,空间又比较大,拷贝起来并不划算
CMS:
标记清除算法
分为几个步骤:
- 初始标记:暂停了所有其他的工作线程(stop the world),进行标记,这一阶段标记进行的会很快。
- 并发标记:并发标记不会暂停其他工作线程(stop the world),但是这样就有可能造成 标记过的状态发生了改变。所以在下一个阶段,进行重新标记。
- 重新标记:会进行stw.就是修正并发标记过程中,用户线程继续运行造成标记变动的。主要用到的就是三色标记里面的增量更新算法。
- 并发清理:不会stw.开启用户线程,同时GC线程开始对未标记的区域进行清扫。如果有新增的对象,会被标记为黑色,不做任何处理。
- 并发重置:重置本次gc过程并发的数据
优点就是并发 停顿时间较少。
但是缺点也是很明显的,在并发标记和并发清理阶段产生的浮动垃圾,只能在下一次才能清理。
还有一个缺点就是该垃圾收集器使用的是 标记清除算法,这样会产生内存碎片。当然我们可以使用参数在执行标记清除后,进行整理 xx:+UseCMSCompactAtFullCollection(默认开启)
CMSFullGCsBeforeCompaction,每隔多少次不压缩的 Full GC 后,执行一次带压缩的 Full GC。默认值为 0,表示每次进入 Full GC 时都进行碎片整理。
而且在并发标记和并发整理阶段,因为是一边回收一边运行,也许还没有回收完成,就引发的full gc,这也就是 concurrent mode failure,这个时候会造成stw,然后之后使用serial old 单线程垃圾收集器进行回收。
关于三色标记:
黑 :所有引用扫描过
灰 :代表访问过,但是至少一个对象的引用没有被扫描过
白:未被访问过
过程:
gc是黑色的,然后初始扫描的时候是灰色的
同理 扫描到B
但是 用户干扰的情况下,指针有可能发生改变,就会产生 多标和露标的现象
针对于B, D的指向变了,变成了A
存活的对象D,竟然被标记为了垃圾!!!
在这个过程中 D被清理掉了
多标和漏标:
针对多标:也就是在并发标记,并发清理阶段,使用三色指针,全部当做黑色。本轮不进行擦除,下一轮进行处理。
针对漏标:被引用的对象当成垃圾错误删除,这个就是严重的bug了。
有两种解决的方案:
- 增量更新(Incremental update):黑色对象一1,就会变成灰色。
- 原始快照(snapshot at the begining,SATB):
删除的时候记录一下引用关系,等并发标记结束之后,我再去扫描一下,看一下你有没有删除。
● CMS:写屏障 + 增量更新
● G1,Shenandoah:写屏障 + SATB
● ZGC:读屏障
关于跨代引用
一个对象,可能被新生代引用,也有可能被老年代引用。
这样产生一个问题,就是新生代回收的过程中,老年代也需要遍历一遍。这种成本就很高
我们在新生代 建立一个记忆集的集合
address记忆 老年代的一块区域(address 的集合) 512字节,这样把整个区域来进行扫描,这样较少index.
写屏障来维护卡表
赋值之前成为写前屏障
赋值之后成为写后屏障
关于G1
G1垃圾收集器两个突出的改进。
(1) 基于标记整理算法,不产⽣内存碎⽚。
(2) 可以精确地控制停顿时间,在不牺牲吞吐量的前提下实现短停顿垃圾回收。
G1将 堆划分为多个大小相等的独立区域Region。
G1还是有 老年代和年轻代,只是逻辑上的概念,并且可以是不连续的了。
关于对大对象的处理,不再是大对象直接进入老年代,有一个单独分配大对象的Region区域叫做Humongous区。
超过Region区的百分之50,就会进入Humongous
阶段:
- 初始标记(initial mark,STW):cms初始标记
- 并发标记(concurrent Marking):cms并发标记
- 最终标记(Remark STW):cms重新标记
- 筛选回收(Cleanup STW):这个阶段会根据Region的回收价值和成本进行排序,根据用户所期望的停顿时间 (JVM参数:-xx:MAXGCPauseMills指定),尽量把GC导致的停顿时间控制在我们指定的范围内。
该算法主要是 复制算法,将一个region存活对象复制到另一个存活的对象中去。
cms会有内存碎片产生,还需要再清理一次。G1几乎没有内存碎片。
尽量把GC导致的停顿时间控制在我们指定的范围内?
主要G1在后台维护了一个优先列表,根据允许的收集时间,选择回收价值最大的Region。一个region花费200ms回收10m垃圾,另一个回收50ms。有限选择50ms的回收。
G1收集器:
YoungGC: 不是eden区域放满了就会马上出发,而是计算现在的回收时间,如果远远小于设置的停顿时间,就会增加eden region区域。接近参数的时候,才会出发YoungGC.
MixedGC:不是FullGC,老年代的堆占有率达到参数(-XX:InitiatingHeapOccupancyPercent)设定的值则触发,回收所有的Young和部分Old(根据期望的GC停顿时间确定old区垃圾收集的优先顺序)以及大对象区,正常情况G1的垃圾收集是先做MixedGC,主要使用复制算法,需要把各个region中存活的对象拷贝到别的region里去,拷贝过程中如果发现没有足够的空region能够承载拷贝对象就会触发一次Full GC。
FullGC:停止系统程序,然后采用单线程进行标记、清理和压缩整理,好空闲出来一批Region来供下一次MixedGC使用,这个过程是非常耗时的。