一、垃圾收集算法
1,分代收集理论
原理
堆内存因对象存活周期不同分为新生代和老年代,分代收集理论就是根据不同年代的特点选择合适的垃圾收集算法,从而提高gc效率。
2,标记-复制算法
原理
将堆内存分为大小相等的两块,每次只使用其中的一块。当这一块的内存使用完后,将还存活的对象复制到另外一块去,在把已满空间内的对象全部清理掉。
图示
![](https://i-blog.csdnimg.cn/blog_migrate/e678a3542c27fa26e20db6e27ca63341.png)
优缺点
优点:清理完后内存整齐,几乎不会有内存碎片。
缺点:每次只能使用内存的一般,内存利用率减半。
3,标记-清除算法
原理
标记存活对象,统一回收所有未被标记的对象。
图示
![](https://i-blog.csdnimg.cn/blog_migrate/1ecbfb9dee9c3dbb68b6bbfaba81431d.png)
优缺点
优点:内存利用占满。
缺点:内存过大,存活对象较高的话,标记效率不高;清理过后可能会产生大量的内存碎片。
4,标记-整理算法
原理
标记存活对象,让所有存活对象向一端移动,然后清除掉端边界以外的所有对象。
图示
![](https://i-blog.csdnimg.cn/blog_migrate/f30e932ac3efe204a1aa6f859d65bf05.png)
优缺点
优点:内存利用占满,内存整齐,几乎不会有碎片产生。
缺点:如果内存过大,存活对象较多,标记+移动效率都不高。
二、垃圾收集器
1,Serial收集器
简介
单线程收集器,整个收集过程STW。
新生代采用复制算法,老年代采用标记-整理算法。
老年代收集器可以作为CMS收集器的后备方案。
收集流程
![](https://i-blog.csdnimg.cn/blog_migrate/6d70eaa5d13968ad993c5a1b067a8d2b.png)
相关参数
-XX:+UseSerialGC:新生代启用Serial收集器。
-XX:+UseSerialOldGC:老年代启用Serial收集器。
2,Parallel收集器
简介
多线程收集器,整个收集过程STW,默认收集线程数==cpu核数。
jdk1.8默认的新生代和老年代收集器。
新生代采用复制算法,老年代采用标记-整理算法。
关注的是吞吐量(更高效率的利用cpu去gc)。
收集流程
![](https://i-blog.csdnimg.cn/blog_migrate/0275a364b95c38d20d8753b647ece69b.png)
相关参数
-XX:+UseParallelGC:新生代启用Parallel收集器。
-XX:+UseParallelOldGC:老年代启用Parallel收集器。
3,ParNew收集器
简介
类似Parallel收集器,主要是用来配合CMS进行垃圾回收。
采用复制算法,只能收集新生代内存。
收集流程
![](https://i-blog.csdnimg.cn/blog_migrate/16c072c24bb2658d354aadd4c85b4420.png)
相关参数
-XX:+UseParNewGC:新生代启用ParNew收集器。
4,CMS收集器
简介
真正意义上的并发收集器,实现了垃圾收集线程与用户线程同时工作。
关注的更多是获取最短的回收停顿时间,注重用户体验。
采用标记-清除算法,只能回收老年代内存。
收集流程
![](https://i-blog.csdnimg.cn/blog_migrate/9b3d787833203c18986555e2cc45c1f2.png)
初始标记:stw。标记的是gc roots直接引用的对象,速度很快。
并发标记:用户线程和垃圾收集线程并发执行。从初始标记对象开始遍历标记整个gc roots对象图的对象。由于用户线程也在执行,此过程就可能会存在多标、漏标、新增对象问题。
重新标记:stw。解决并发标记过程中的多标、漏标、新增对象问题。主要是解决漏标问题。
并发清理:用户线程和垃圾收集线程并发执行。清理内存中未被标记的对象。由于用户线程也在执行,此过程就可能会存在新增对象问题。
并发重置:用户线程和垃圾收集线程并发执行。重置本轮gc的所有标记数据,清理标记。
相关参数
-XX:+UseConcMarkSweepGC:老年代启用CMS收集器。
-XX:+CMSParallelInitialMarkEnable:设置初始标记过程多线程执行,缩短stw时间。
-XX:+CMSParallelReMarkEnable:设置重新标记过程多线程执行,缩短stw时间。
-XX:CMSInitiatingOccupancyFraction:设置触发full gc的老年代内存占用比例,默认92%。
-XX:+UseCMSInitiatingOccupancyOnly:只使用设定的回收阈值(-XX:CMSInitiatingOccupancyFraction设定的值),如果不指定,jvm仅会在第一次使用设定值,后续会自动调整。
5,G1收集器
简介
![](https://i-blog.csdnimg.cn/blog_migrate/039dac85484044b280b449e872c4dea5.png)
将java堆分为多个大小相等的region,最多2048个,一般region大小等于堆大小除以2048。
保留了分代概念,但不再是物理划分了,而是一个个region。
有专门分配大对象(一个对象超过了一个region大小的50%)的region叫Humongous区。一个Humongous对象一般会横跨多个region存放。
region的区域功能可能会动态变化。一个region之前可能是新生代,可能一次gc之后就会变为老年代。
收集流程
![](https://i-blog.csdnimg.cn/blog_migrate/6fdd17348ada1477b838ca8ce350e1b5.png)
初始标记:stw。标记的是gc roots直接引用的对象,速度很快。
并发标记:用户线程和垃圾收集线程并发执行。从初始标记对象开始遍历标记整个gc roots对象图的对象。由于用户线程也在执行,此过程就可能会存在多标、漏标、新增对象问题。
最终标记:stw。解决并发标记过程中的多标、漏标、新增对象问题。主要是解决漏标问题。
筛选回收:stw。对每个region的回收价值和成本计算排序,根据用户期望的stw停顿时间,制定回收计划,优先回收一部分region,达到停顿阈值后,停止回收,剩余的垃圾region待下次gc回收。
GC分类
Young GC:并不是现有Eden区放满就马上触发,G1会计算现在回收Eden区大概需要的时间,如果远远小于预期最大停顿时间,会增加年轻代的region继续存放新对象,不会马上做Young GC,直到下一次Eden放满,重复计算判断过程,当计算的回收时间接近预期最大停顿时间时,触发Young GC。
Mixed GC:老年代的内存占有率达到-XX:InitiatingHeapOccupancyPercent参数设定的值则触发Mixed GC。回收所有的年轻代和部分老年代(根据预期停顿时间决定回收多少)以及大对象区。
Full GC:G1在Mixed GC过程中需要把各个region中的存活对象复制到别的region,如果没有足够大的region可以存放,就会触发Full gc。此过程stw,采用单线程进行垃圾收集,非常耗时。
相关参数
-XX:+UseG1GC:启用G1收集器
-XX:G1NewSizePercent:设置新生代初始内存占用比例,默认5%。
-XX:G1MaxNewSizePercent:设置新生代最大内存占用比例,最大不能超过60%。
-XX:MaxGCPauseMills:设置预期停顿时间,默认200ms。
-XX:InitiatingHeapOccupancyPercent:设置Mixed GC触发的老年代空间在整堆的占用比例阈值,默认45%。
-XX:G1MixedGCLiveThresholdPercent:设置一个region回收的存活对象比例,默认85%。低于这个值才会回收。
三、三色标记算法
1,原理
把gc roots可达性分析遍历过程中遇到的对象,按照"是否访问过"这个条件标记成三种颜色。
黑色:表示对象及其所有的引用都已经被垃圾对象扫描过,是完全存活的对象,不会被再次扫描。
灰色:表示对象已经被垃圾收集器扫描过,但这个对象至少还有一个引用没有被扫描。
白色:表示对象尚未被垃圾收集器扫描过。可达性算法刚开始时,所有对象都是白色的,分析结束时还是白色的对象即为不可达对象,也就是垃圾对象。
2,应用
CMS垃圾收集过程一重新标记。
多标解决:下一次gc清理。
新增对象解决:直接标记为黑色,使其变为浮动垃圾,下一次gc清理。
漏标解决:写屏障+增量更新算法解决。黑色对象插入新的白色对象的引用关系后,将新插入的引用记录下来,并发标记过后,再以这些记录中的黑色对象为根,重新扫描标记一次。
G1垃圾收集过程一最终标记。
多标解决:下一次gc清理。
新增对象解决:直接标记为黑色,使其变为浮动垃圾,下一次gc清理。
漏标解决:写屏障+SATB(原始快照)。将要插入引用的白色对象直接标记为黑色,使其在本轮gc清理中活下来(变其为浮动垃圾),并发标记过后,重新扫描标记即可。