JVM垃圾回收算法和垃圾回收器
一、垃圾回收算法
1、标记复制算法
会把内存分为相同的2个部分,每次回收,会把存活的对象移动到另一边,回收当前使用的空间。分配的内存被分成2份,实际使用空间变成正常的一半。但是不会出现垃圾碎片。
2、标记清除算法
标记存活的对象,把未标记的回收。回收后内存不是连续的,会产生大量的不连续的碎片,标记对象的时候效率低。
3、标记整理(压缩)算法
会把存活的对象移动到一起,清除边间外的垃圾对象,效率低
二、垃圾回收器
1、Serial和SerialOld
Serial是新生代,SerialOld是老年代的回收期,串行化执行,最简单的单线程的收集器,会STW。
CMS收集器如果空间不够无法进行FULL-GC,就会用SerialOld进行回收。
Serial使用标记复制,SerialOld使用标记整理算法
2、Parallel Scavenge收集器 Parallel Old
使用多线程,其他的和Serial相同,关心吞吐量,会自动调整参数设置吞吐量的大小,停顿的时间,提供最优的吞吐量。JDK8默认的收集器
新生代采用复制算法,老年代使用标记整理算法。
3、parNew收集器
使用多线程,其他的和Serial相同。使用复制算法
新生代的收集器,可以搭配CMS收集器使用,
4、CMS收集器
为了提高用户的体验,提供最短的STW,并发收集,可以让垃圾回收线程和用户线程同时使用,标记清除算法,默认情况下是老年代内存达到92%会执行FullGC。
分为以下阶段:
(1)初始标记:会STW,但是速度非常快,标记GCROOT能引用的对象,可以用-XX:+CMSParallellnitialMarkEnabled参数开启多线程执行。
(2)并发标记:根据GCROOT遍历所有对象,过程比较慢,不会STW,会和用户线程一起执行。
(3)重新标记:因为并发标记中垃圾回收线程会和用户线程一同执行,可能会出现,被标记为垃圾对象的现在不是垃圾对象了会对产生变动的重新进行标记。会STW,比初始标记时间长,并发标记时间短。可以用 -XX:+CMSParallelRemarkEnabled 参数开启多线程重新标记。(存活的对象现在是垃圾对象,可达变不可达,是不会被重新标记的,这个是浮动垃圾)
(4)并发清理:和用户线程一同执行,对未标记的对象清理,这个阶段如果有新添加的对象,会被标记为黑色。
(5)并发重置:重置本次标记的对象,与用户线程一同运行。
优点: 并发执行,低停顿,用户体验较好
缺点:
会产生浮动垃圾只能等待下一次回收
占用CPU资源
标记清除会产生空间碎片,可以通过开启参数,做完发FullGC自动整理碎片( -XX:+UseCMSCompactAtFullCollection),可以通过参数设置多少次FullGC整理一次内存碎片( -XX:CMSFullGCsBeforeCompaction)
在FullGC执行前,可以先进行一次YGC来减少内存对象的引用,加快CMS在FullGC时标记的速度(-XX:+CMSScavengeBeforeRemark)
5、G1收集器
G1收集器把java的堆分为多个大小相等的区域Region,最多可以有2048个Region。一个Region = 堆容量/2048。适合大内存。
年轻代(eden,survivor),占总容量的5%,但是在JVM运行的时候,会不断地给年轻代调整Region最多占60%可以通过参数调整-XX:G1MaxNewSizePercent。
老年代:
Humongous区:专门用来存储大对象,如果这个对象超过Region的50%就是一个大对象
在进行GC的时候年轻代、老年代、大对象区都会被回收。
(1)初始标记:会STW,记录GCROOT可以引用的对象,速度快
(2)并发标记:根据GCROOT遍历所有对象,过程比较慢,不会STW,会和用户线程一起执行。
(3)最终标记:和CMS的重新标记相同。
(4)筛选回收:根据设置的GC停顿时间(-XX:MaxGCPauseMillis)来设置回收计划,会对各个Region的进行回收的时间计算,优先选择回收价值高的Region来进行回收。默认的回收次数是8次,可以通过(-XX:G1MixedGCCountTarget)控制回收几次。会STW。
年轻代和老年代都是使用的复制回收算法,把一个Region复制到另一个Region中,不会出现空间碎片
YGC:YCG并不是Eden区满了就去回收,会先计算回收Eden需要多长时间,如果回收时间小于设置的回收时间,就继续给Eden分配Region,直到下一次满了在判断回收时间,如果接近设置的回收时间,就触发YGC.
MixedGC:老年代的占有率达到回收的默认45%,就会执行MixedGC,回收所有的年轻代、部分老年代和大对象,在这个过程中,如果剩下的空Region放不下存活的对象,就会触发FGC。
FGC:停止系统程序,采用单线程进行标记、清理、压缩,这个过程非常耗时。
三、三色标记算法
1、黑色
代表这个对象所有的引用都被扫描完成。
2、灰色
这个对象所有的引用可能还有至少1个对象引用没有被扫描到
3、白色
还没有扫描的会被标记为白色,如果在GC并发标记完成后,还是白色,就代表这个对象不可达,是个垃圾对象
在并发标记的过程中,用户线程和标记线程是同时运行的,这个时候就会出现已经是垃圾对象的被重新引用,但是它已经被标记为白色,或者某个对象由存活对象变成垃圾对象,对象的引用发生了改变,这个时候就会出现多标或者漏标
4、漏标
漏标会把引用的对象当成垃圾回收掉,可以用增量更新或者原始快照实现
(1)增量更新-CMS
黑色对象指向白色对象的引用时,就把这个新插入的引用记录下来,等并发扫描结束后,在把这些记录过引用关系的跟节点重新扫描。
(2)原始快照(SATB)-G1
灰色对象要删除指向白色对象的引用,把这个删除的引用记录下来,在并发扫描结束后,在把这个灰色的对象重新扫描,这样就能扫描到这个对象,然后把这个白色的对象标记为黑色,等待下一次GC,这个白色的对象有可能是浮动垃圾。
(3)增量更新和原始快照都需要和写屏障来实现
写前操作,写后操作
写屏障实现增量更新: 把新的应用对象记录下来
写屏障实现SATB: 把删除的引用存放到队列中
5、多标-浮动垃圾
执行并发标记的时候有些被扫描过得存活对象,已经销毁,它已经被标记为黑色,这些对象就是浮动垃圾,还有就是并发清理开始后产生的对象会被当成黑色,但是他有可能是垃圾对象,这些都是浮动垃圾。