1. JVM垃圾回收算法
1.1 标记清除算法
最基础的垃圾回收算法,分为两个阶段,标注和清除。标记阶段通过可达性分析算法标记出所有存活的对象,在清除阶段对所有未标记的对象进行统一回收, 如图:
缺点: 内存碎片化严重,后续可能发生大对象不能找到可利用空间的问题;其次,两个阶段效率都不高
1.2 标记复制算法
为了解决 Mark-Sweep 算法内存碎片化的缺陷而被提出的算法。按内存容量将内存划分为等大小的两块。
在同一时间只会使用其中一块内存区域来分配对象。在发生GC时,首先通过根可达性分析算法判断存活对象,并且将所有的存活对象移动到另一块未使用的内存区域,最后对于前面的这块使用了的内存区域中的对象统一回收。如图:
优点:实现简单,内存效率高,不易产生碎片
缺点:可用内存被压缩到了原本的一半,且存活对象增多的话,复制算法的效率会大大降低
1.3 标记整理(压缩)算法
结合了以上两个算法,为了避免缺陷而提出。标记阶段和 Mark-Sweep 算法相同,标记后不是清理对象,而是将存活对象移向内存的一端。然后清除端边界外的对象。如图:
2. JVM垃圾收集器
2.1 什么是STW
stw 是stop the world的简称,当JVM在进行垃圾回收时,会暂停用户线程,以便正确标记对象的状态是垃圾还是非垃圾,方便后续进行垃圾回收。如果没有STW机制,用户线程与JVM垃圾回收线程同时运行时,会出现,之前被垃圾回收线程标记为非垃圾的对象,等用户线程执行完又变成垃圾了,这样JVM垃圾回收将永远无法停止,因此需要暂停用户线程,将一批标记为垃圾的对象清除后,再让用户线程执行。
2.2 垃圾收集器
常见的垃圾收集器有CMS,G1,ZGC,G1是用的最多的。在JDK1.8及以前,JVM使用分代模型,1.8之后使用分区模型
2.2.1 UseSerialGC
当用户线程执行到安全点,JVM执行引擎暂停用户线程,开启一个GC线程,对于年轻代,使用标记复制算法进行垃圾收集,对于老年代,采用标记整理算法进行垃圾收集,缺点:GC是单线程,效率低
2.2.2 UseParNewGC
2.2.3 UseParallelGC/ UseParallelOldGC
新生代使用 Parallel Scavenge GC 老年代使用 ParallelOld GC
2.2.4 UseConcMarkSweepGC
使用-XX:UseConcMarkSweepGC, 新生代使用ParNew GC, 老年代使用CMS GC与Serial Old GC的收集器组合,Serial Old 将作为CMS出错后的后备收集器
CMS并发标记清除的过程:
- 初始标记
只将GC root 所直接引用的对象标记出来,其他的对象不管 - 并发标记
这个阶段用户线程可以执行,同时,将GC root 可达的所有对象标记出来(一边产生垃圾,一边标记垃圾对象,不严谨) - 重新标记
暂停用户线程,将并发标记阶段错标,漏标的对象重新进行标记 - 并发清理
注:图片来自https://www.processon.com/view/5dec50d7e4b0e2c298a6bd60?fromnew=1
CMS之所以采用这么复杂的标记清除过程,是为了解决STW时间过长的问题
2.2.5 G1垃圾收集器
分代收集思想: 不管房间有多大,这个房间里面的垃圾都清除掉
分区收集思想: 不管你房间有多大,我把房间分成多个小块,先清理出一块区域给你用,后面再继续清理其他区域,用户不用等太久
其他参考:
https://www.processon.com/view/5f7fdf845653bb06eff0a16d?fromnew=1