这一节咱们来总结一下JVM垃圾收集器方面的东西。
算法
垃圾回收器
判断对象引用是否失效
对象生存判断算法
- 引用计数法
给对象中添加一个引用计数器,每当一个地方引用到这个对象的时候,计数器值就加1,当引用失效时,计数器的值就减1,当计数器值变为0时,便说明该对象不可能再被使用了。
优势:实现简单,断定效率较高。
缺点:当出现对象之间的相互循环引用时,即两个类中都存在引用字段分别引用着对方的时候,在回收过程当中这时该算法无效。
- 可达性分析算法
为了克服引用计数法的弊端,如今比较主流的实现算法是可达性分析算法。该算法的基本思想是经过一系列的成为“GC Roots”的对象做为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链。若是在一次的搜索中,一个对象到GC Roots没有任何的引用链相连,则说明此对象是不可用的。具体如图所示

GC Roots的断定:
一、虚拟机栈(栈帧中的本地变量表)中引用的对象
二、方法区中静态属性引用的对象
三、方法区中常量引用的对象
四、本地方法栈中JNI(即通常说的Native方法)引用的对象多线程
引用判断过程
判断引用是否无效的过程分为三个阶段
一、当JVM进行垃圾收集时,JVM使用可达性分析算法进行分析,若是对象在进行可达性分析后发现没有与GC Roots相链接的引用链,此时该对象将被第一次标记,并进行一次筛选,筛选的条件是此对象有没有必要执行finalize()方法,若是对象没有覆盖该方法,或者该方法已经被虚拟机调用过了,虚拟机将这两种状况都视为“没有必要执行”。
二、若是该对象被断定为有必要执行finalize()方法,那么对象将会被放置到一个叫作F-Queue的队列中,并在稍后由一个由虚拟机自动创建的、低优先级的Finalizer线程去执行它。这里所谓的执行是指虚拟机会触发这个方法,但并不承若会等待它运行结束。由于一个对象可能在finalize()方法中执行缓慢,或者发生了死循环,这将致使该队列中的其余对象长期处于等待阶段,甚至致使整个内存系统的奔溃。
三、F-Queue中的标记筛选。
finalize()方法是对象逃脱死亡命运的最后一次机会,而后GC将对F-Queue中的对象进行第二次小规模的标记。若是对象在finalize()方法中成功拯救了本身,即与引用链上的任何一个对象创建关联,那么在第二次标记的时候,该算法将被移出F-Queue的集合,若是对象这个时候尚未逃脱,那基本上它就真的被回收了。并发
垃圾收集算法简介
目前比较主流的垃圾收集算法有四种:标记-清除算法、复制算法、标记-整理算法、分代收集算法。具体分析对好比下:性能
分类 |
标记-清除算法(Mark-Sweep) |
复制算法(Coping) |
标志-整理算法(Mark-Compact) |
分代收集算法(Generational Collection) |
进行整理 |
否 |
是 |
是 |
是 |
算法实现过程 |
该算法分为两个过程:标记和清除。先标记出全部须要回收的对象,在标记完成后统一回收全部被标记的要回收的对象。 |
将内存按容量划分为大小相等的两块区域,每次使用其中的一块,当一块的内存用完了,执行GC算法时将还存活的对象整理复制到另一块上,而后清理全部的内存块。 |
该算法分为两个过程:标记和整理。首先标记出全部须要回收的对象,而后让存活的对象都向内存的一端移动,而后直接清除掉端边界之外的内存。 |
根据对象存活周期的不一样将内存划分为几块,通常是划分为新生代和老年代,而后根据各个年代的特色采用不一样的最适当的收集算法。 |
优势 |
简单,易于实现 |
内存分配时算法不产生内存碎片 |
内存分配时算法不产生内存碎片,也比较易于实现 |
分代收集,效率较高 |
缺点 |
一、效率低 二、会产生大量不连续的内存碎片 |
空间消耗太大,内存被压缩为原来的一半 |
算法复杂度大,执行步骤较多 |
算法复杂度大,执行步骤较多 |
垃圾收集器
常见的JVM垃圾收集器有七种,具体以下图所示:

spa
新生代垃圾收集器
- Serial
Serial收集器是单线程的一个收集器,但它的单线程的意义是它只会使用一个CPU或者一条收集线程去完成垃圾收集工做,更重要的是在它进行垃圾收集的时候,必须暂停其余全部的工做线程,直到它收集结束。
分代收集算法:新生代单线程采用复制算法,并暂停全部用户线程;老年代单线程采用标记-整理算法,并暂停全部用户线程。
- ParNew
ParNew收集器是Serial收集器的多线程版。其基本操做和Serial算法基本一致。该收集器通常搭配CMS收集器进行工做。‘
分代收集算法:新生代采用复制算法,并暂停全部用户线程;老年代采用标记-整理算法,并暂停全部用户线程。
- Parallel Scavenge
Parallel Scavenge收集器是也与ParNew算法十分类似,可是与其余收集器的关注点大可能是尽量缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器目的是达到一个可控制的吞吐量。吞吐量就是CPU用于运行用户代码的时间与CPU总消耗的时间的比值。即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间),举个例子,虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。
GC自适应调节策略:JVM会根据当前系统的运行状况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大吞吐量。Parallel Scavenge收集器能够搭配自适应调节策略。
分代收集算法:新生代采用复制算法,并暂停全部用户线程;老年代采用标记-整理算法,并暂停全部用户线程。
老年代垃圾收集器
- Serial Old
Serial Old是Serial算法的老年代版本,一样是一个单线程收集器。该收集器主要是给Client模式下的虚拟机使用的。
分代收集算法:新生代单线程采用复制算法,并暂停全部用户线程;老年代单线程采用标记-整理算法,并暂停全部用户线程。
- Parallnel Old
Parallnel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。
- CMS
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。该收集器是基于”标记-清除“算法实现的。
CMS收集器的收集过程分为如下4个步骤:
一、初始标记(Stop the World,标记GC Roots能直接关联到的对象)
二、并发标记(进行GC Roots Tracing的过程)
三、从新标记(Stop the World,休整并发标记期间因用户程序继续运行而致使标记产生变更的那一部分对象的标记记录)
四、并发清除(并发清除无用的对象)
缺点:
a、CMS收集器对CPU资源很是敏感,并发阶段占用的线程资源较多。
b、CMS收集器没法处理浮动垃圾。由于CMS并发清理阶段用户线程还在运行着,因此也会有相应的垃圾产生,这部分垃圾CMS没法在这次的收集中处理掉它们。
c、CMS收集器因为是基于“标记-清除”算法,故会产生较多的内存空间碎片。
新生代和老年代垃圾收集器
- G1
G1(Garbage-First)收集器所具有的特色:
一、并行和并发:使用多个CPU来缩短Stop-The-World的时间,部分垃圾收集器本来须要停顿Java线程执行的GC动做,G1收集器仍然能够经过并发的方式让Java程序继续执行。
二、分代收集
三、空间整合:标记-整理算法。
四、可预测的停顿。追求低停顿,并创建可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片断内,消耗在垃圾收集上的时间不得超过N毫秒,达到了实时Java的垃圾收集器。
G1收集器分代策略:
G1收集器将整个Java堆划分为多个大小相等的独立区域(Region)。G1收集器之因此能够有计划地避免在整个Java堆中进行全区域的垃圾收据,是由于G1收集器跟踪各个Region里面的垃圾堆积的价值大小(回收得到的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据容许的收集时间,优先回收价值最大的Region。即Grabage-First。
在G1收集器中,Region之间的对象引用以及其余收集器中的新生代与老年代之间的对象引用,虚拟机都是经过
Remembered Set来避免全堆扫描的。G1中每一个Region都有一个与之对应的Remembered Set。在新建对象时,JVM会将相关的引用信息记录到被引用对象所属的Region的Remembered Set中。当进行回收时,在GC根节点的枚举范围中加入Remembered Set便可保证不对堆进行扫描也不会有遗漏。
G1收集器的手机阶段也分如下几个步骤:
一、初始标记(只是标记一下GC Roots能直接关联到的对象,并修改能够得Region中建立新对象,这阶段须要停顿线程,但耗时很短)
二、并发标记(从GC Roots开始对堆中对象进行可达性分析,找出存活对象)
三、最终标记(修正在并发标记期间因月洪湖程序继续运行而致使标记产生变更的那一部分标记记录)
四、筛选回收(首先对各个Regin的回收价值和成本进行排序,根据用户所期待的GC停顿时间指定回收计划,回收一部分Region)
最后,咱们总结一下JVM中的垃圾收集器:线程
分类 |
所属分代 |
使用线程 |
使用算法 |
Serial |
新生代 |
单线程 |
复制(新)、标记-整理(老) |
ParNew |
新生代 |
多线程 |
复制(新)、标记-整理(老) |
Parallel Scavenge |
新生代 |
多线程 |
吞吐量优先算法 |
Serial Old |
老生代 |
单线程 |
复制(新)、标记-整理(老) |
Parallel Old |
老生代 |
多线程 |
复制(新)、标记-整理(老) |
CMS |
老生代 |
多线程 |
标记-清除算法(初始标记、并发标记、从新标记、并发清除) |
G1 |
新生代&&老生代 |
多线程 |
标记-整理算法(初始标记、并发标记、最终标记、筛选回收) |