serial parallel parNew cms g1 全是分代思想的实现者,所以分代思想的优化方式都是通用的,标记算法全是可达性算法实现。
serial
单线程实现,整个gc过程会STW
回收算法使用标记整理
适合单CPU 小内存,1G以内
年轻代 serial 老年代 serial old
parallel
多线程实现,整个gc过程会STW
回收算法使用标记整理
适合多cpu 小内存,1-4G
年轻代 parallel 老年代 parallel old
parNew
同parallel一样的实现, 存在是因为parallel无法和cmd配合使用,所以就开发一个可以和cms配合的parallel,命名为parNew
仅用于年轻代
cms
concurrent mark sweep 并发标记 分多阶段回收,仅部分阶段会STW, 注重用户体验
回收算法使用标记删除,删除快,但需要内存碎片整理
适合大内存4-8G
GC各阶段:
1、初始标记 stw 标记gcroot直接引用对象,第一层
2、并发标记 遍历上阶段标记的对象所有引用
3、重新标记 stw 处理并发时,多标漏标的对象
4、并发清理 删除标记对象
5、并发重置
多标与漏标
在并发标记阶段,标记和用户线程是并行的,引用的重新赋值会产生多标漏标问题。
1、若已经标记的对象被赋值为null,按正常来说应该被回收,但是却已经被标记过了,称为多标,多标不会对系统产生影响,等待下次gc即可。
2、若黑色对象内部赋值了新的引用, 由于黑色对象不会被遍历,新引用对象就会漏掉,若新引用对象是白色,则可能被删除,产生严重bug,必须处理。
漏标处理,写屏障-增量更新, 在用户赋值时,将新对象加入容器,等待重新标记。
内存整理,由于是标记删除,会产生碎片,需要进行内存整理,默认每次gc后整理,可配置n,在n次后进行整理。内存整理是stw。
一边gc,一边运行系统,当上一次还没完,又触发gc后,就会stw, 使用serial old stw进行垃圾收集。
g1
使用了region模式,默认划分了2048个region,分代仅是逻辑概念, 物理分区不再按照分代思想。新增了Humongous类型分区,专门用于存放超过50%region的大对象,在mixed/full gc中清理。每个region的类型是动态的可以是eden survive old humongous等
通过-XX:G1NewSizePercent设置年轻代占比(默认5%)年轻代会根据gc动态增长,最大值通过-XX:G1MaxNewSizePercent设定,默认60%。e区和s区比例不变。年轻代老年代的转移仍然按照分代思想, 除了大对象外。
采用标记复制算法,每个region独立回收。回收策略会根据参数-XX:MaxGCPauseMillis(期望的STW时间ms)来制定。
适合8G以上的内存
GC各阶段
1、初始标记, 同cms,stw,标记gcroot直接引用对象
2、并发标记,同cms
3、最终标记,同cms并发标记,stw
4、筛选回收,stw,对各region进行回收评估,选择满足期望STW时间的region回收,将region内存活对象转移到新的region。由于采用的是复制算法,必须stw,不同于cms。
收集器内部会对每个region评估收集耗时(通过内存大小,对象数量大致可计算时间),再根据期望的STW时间,可计算出本次gc可以回收多少个region。
收集器维护了一个优先回收的region列表, 实现了尽可能短的时候回收更多的region。
期望STW时间不能太低,否则每次gc回收不到足够的内存,会造成频繁gc,引发full gc
gc类型
Young GC, 当回收当前年轻代的时间远小于期望STW时间时, 会直接扩容年轻代,直到满足期望的STW值时,开始Young GC。
Mixed GC,当老年代的使用率达到-XX:InitiatingHeapOccupancyPercent时会触发,回收所有的年轻代、部分老年代和大对象区,若空间不足会触发full gc。
Full GC,暂停系统, 使用单线程进行回收,同serial,回收到足够的内存后,才开始mixed gc。
三色标记
黑,对象自身,下级引用,全部已经被遍历过了。
灰,自身与下级只遍历过一部分,没有完全遍历完。
白,完全没遍历过
遍历结束后只会存在,黑白两色对象,白色对象没被遍历,说明不可达,可进行回收。
三色标记在并发阶段引发的问题及解决
并发阶段:用户线程和gc线程同时执行,用户线程执行的对象引用修改会影响到标记的准确性
多标,应该为白色,却标了黑色,不会被gc回收
在并发过程中产生的垃圾,如删除对象引用,或栈清空,由于黑色对象不会被访问,所以该批对象躲过了gc。
这些对象称为浮动垃圾
多标不会对系统造成影响,等待二次gc时回收即可。
漏标,应该为黑色,却标了白色,会被gc回收。
用户给黑色对象新引用了白色对象black.a = white,该白色对象是从某个灰色对象移过来的,还未被标记, 被黑色引用后,失去了被标记的机会。
漏标会造成误删除,必须解决
CMS: 写屏障-增量更新,在黑色对象引用新对象时(增量),将引用对象加入容器,等重新标记时,再次扫描。
G1:写屏障-原始快照SATB,在灰色对象置空引用对象时(删除),将置空的引用对象加入容器,等并发标记完后,再次扫描。
读写屏障
类似于aop思想, 在get set中,插入pre post方法
安全点
在代码中指定一些点,当线程运行到这些点的时候,状态是确定的,可以挂起线程,进行gc。
安全点位置:
方法返回之前
调用方法之后
抛出异常的位置
循环的末尾
安全区域
在一段代码中,引用关系不会发生变化,如sleep 中断等。