一、垃圾收集算法
目前的垃圾收集算法都是基于垃圾分代收集理论,包括:标记-复制、标记清除、标记-整理算法。其中标记-复制算法的速度远远大于标记-清除和标记-整理算法(10倍)
1、标记-复制算法
标记-复制算法将内存分成相同的两个部分,每次只用其中的一块内存。当一块内存用完后,会将还存活的对象复制到另一块中,然后将使用的这块内存空间清除掉。
标记-复制算法主要用在年轻代。
2、标记-清除算法
标记-清除算法分为标记和清除两个部分。该算法先标记出存活的对象,回收所有未被标记的对象(也可以标记未存活的对象)。
标记-清除算法有两个明显的问题,第一个是容易产生内存空间碎片,第二个就是需要标记的对象太多,效率低。
3、标记-整理算法
标记-整理算法主要是用于老年代,它的前面和标记-清除算法步骤一样,不同点在于标记-整理算法会将所有标记的对象移动到一端,然后清理掉对象边界以外的内存。
二、垃圾收集器
目前的垃圾收集器经过多年的发展,已经有了多种垃圾收集器,主要有Serial和SerialOld(串行)垃圾收集器、Parallal 和Parallal Old(Parallal Scavenge 并行)垃圾收集器、
ParNew和CMS垃圾收集器,以及G1和ZGC等。
1、Serial垃圾收集器
Serial垃圾收集器比较老,它是一种单线程垃圾收集器,在进行垃圾回收时必须Stop the worls(用户程序暂停)。它是CMS垃圾收集器的备用。
2、Parallal Scavenge垃圾收集器
Parallal 是并行垃圾收集器,默认的线程是cpu核数,线程数这个也可以设置。Parallal 垃圾收集器和Serial垃圾收集器在年轻代使用的是标记-复制算法,在年老代使用的是标记-整理算法。
Parallal 垃圾收集器关注的焦点是吞吐量(JDK8默认垃圾收集器),CMS垃圾收集器关注的焦点是用户体验。
3、ParNew收集器
年轻代使用的是标记-复制算法,在年老代使用的是标记-整理算法。
ParNew垃圾收集器和Parallal 垃圾收集器其实没多大变化,最主要的在于ParNew垃圾收集器可以和CMS垃圾收集器一起使用。
4、CMS(Concurrent Mark Sweep)收集器—重点
CMS垃圾收集器关注的重点是用户体验,它的主要目的是尽可能的降低停顿时间
初始标记:这个过程很短,只是根据gc roots标记直接引用的对象。
并发标记:在这个过程中,会根据gc roots标记直接引用的对象继续标记整个对象链条。此时用户线程是可以继续运行的,不需要线程停顿,但是会导致有的对象状态变化。
重新标记:这个阶段是多线程运行,是为了解决在并发标记阶段出现的对象状态发生变化的情况。用的算法是三色标记的增量更新算法。
并发清理:GC线程清除所有未标记的对象区域。此时用户线程是运行的,如果有新的对象产生,会被标记成黑色(三色标记算法下文)。
并发重置:清除所有标记,准备下次GC
CMS垃圾收集器缺点:
1、对CPU的资源需求很敏感。
2、在并发标记和并发清理阶段会产生浮动垃圾。
3、因为采用的是标记-清除算法,会产生大量垃圾碎片。
4、容易触发 full gc,出现concurrent mode failure。这时就会使用Serial垃圾收集器。
三、三色标记算法—重点
根据可达性分析,根据GC过程中否访问过,将对象标记为三种颜色:
黑色:根对象,或者该对象与它的引用的对象都被扫描过。
灰色:对象本身被扫描,但还没扫描完该对象中引用的对象。
白色:未被扫描对象,白色为不可达对象,即垃圾对象。
三色标记算法缺点:
1、多标-浮动垃圾
主要包括原本是引用的非垃圾对象后来线程销毁变成了垃圾对象,和在并发标记和并发清理产生的新对象。
2:漏标-读写屏障
灰色对象指向白色对象的引用消失了,然后一个黑色的对象重新引用了白色对象。
解决办法是增量更新和原始快照。
增量更新是当新建一个指向白色对象的引用关系时,保存这个关系,然后在并发结束之后,将这个白色对象标记为黑色。
原始快照是当一个灰色对象删除指向一个白色对象时,将这个引用关系保存下来,并发结束之后,将白色对象标记为黑色。
读写屏障和切面拦截相似,就是在操作前后添加一些操作,保存这些引用关系。