说起垃圾收集器,咱们先画个图
YOUNG 年轻代
Serial
特点:
Serial收集器是HotSpot运行在client模式下的默认新生代收集器,它的特点是只用一个cpu,一个线程去完成GC工作,且在进行垃圾收集时必须暂停其他所有的工作线程(STW)
年轻代串行回收
ParNew
特点:
服务器多核的情况下效率要比Serial要高,但是并不能解决STW,单核服务器效率和Serial效率差不多
ParNew收集器是Serial的多线程版本,包括控制参数、收集算法、STW、对象分配规则、回收策略等都与Serial完全一样,(VM启动CMS收集器的默认新生代收集器)
年轻代 配合CMS的并行回收
Parallel Scavenge
特点:
与ParNew类似,Parallel Scavenge也是使用复制算法,也是并行多线程收集器,但与其他收集器关注尽可能缩短垃圾收集时间,Parallel Scavenge更关注系统吞吐量;
系统吞吐量 = 运行用户代码时间/(运行用户代码时间+垃圾收集时间)
年轻代并行回收
OLD
CMS
concurrentMarkSweep老年代并发垃圾回收器:垃圾回收和应用程序同时运行,降低STW的时间(200ms)CMS的问题问题比较多,所以现在没有一个版本默认时CMS,只能手工指定
CMS既然是MarkSweep,就一定会有碎片化的问题,碎片达到一定程度,CMS的老年代分配对象分配不下的时候,使用Serial Old 进行老年代回收
phases 执行过程
1、initial mark
初始标记,标记出根对象
这个过程会触发STW,由于是根对象,所以比较少,STW时间会很短
2、concurrent mark
并发标标记
耗时最长,是和工作线程同时,不会触发STW
这个过程会产生另一个问题,有可能在标记的过程中,对象不是垃圾对象了
3、remark
重新标记
对并发标记的过程中,被修改过的对象重新标记
标记的对象个数较少,会触发STW,耗时较短
4、concurrent sweep
并发清理,会启动多个线程进行清理
算法:
CMS使用 三色标记 + Incremental Update
1、当一个白色对象被一个黑色对象引用
2、将黑色对象重新标记为灰色,让collector重新扫描
Serial Old
特点:
和新生代的Serial收集器差不多,新生代中使用的算法是mark-sweep(标记-清除)算法,而老年代中使用 的是标记-整理算法
此算法有一个致命的缺点,单线程清理
单线程串行化收集器
Parallel Old
特点:
老年代的垃圾回收,但与Parallel Scavenge不同的是,它使用的是Mark-Compact(标记整理)
注重于吞吐量及CPU资源敏感的场合
``````properties
G1
G1的分代区域不是固定的,如eden区经过垃圾回收后有可能是old区
region 分区
1、eden
2、survivor
3、old
4、humongous 大对象 (超过region区的50%为大对象)
特点
1、并发收集
2、压缩空闲空间不会延长GC的暂停时间
3、更易预测的GC暂停时间
4、使用不需要实现很高的吞吐量的场景,高响应
尽量保持不要FullGC,因为jdk10之前FullGC是单线程进行的
软件架构中两种重要的思想
1、分而治之
2、分层
1、collection set = Cset
存储需要被回收的垃圾集合
垃圾收集器不需要扫描整个堆,找到cset直接回收即可
2、RememberedSet = RSet(hashset)
记录了其他Region中的对象到本Region的引用
垃圾收集器不需要扫描整个堆找到谁引用了当前分区中的对象,只需要扫描RSet即可;
Card Table
由于做YGC时,需要扫描整个Old区,效率非常低,所以JVM设计了CardTable,如果一个Old区
CardTable中有对象指向Y区,就将它设为Dirty,下次扫描时,只需要扫描Dirty Card在结构上,Card table用BitMap实现
新老年代比例
5%~60%
不需要手动指定,G1会按照实际情况动态调整
GC触发时机:
YGC
eden空间不足
多线程并行执行
FGC
Old空间不足
System.gc()
算法:
使用三色标记+SATB
白色:未被标记的对象
灰色:自身被标记,成员变量未被标记
黑色:自身和成员变量已标记完成
漏标:B-C消失,A-D建立
打破漏标
1、incremental update 增量更新(CMS使用此算法)
关注引用的增加,把黑色重新标记为灰色,下次重新扫描属性
2、SATB snapshot at the beginning 关注引用的删除(G1)
在起始的时候做一个快照
当B->D消失时,要把这个引用推到GC的堆栈,保证D还能被GC扫描到
为什么G1要使用SATB
灰色到白色引用消失时,如果没有黑色指向白引用会被push到堆栈
下次扫描时拿到这个引用,由于有RSet的存在,不需要扫描整个堆区查找指向白色的引用,
效率比较高,SATB配合RSet浑然天成