JVM之GC算法的实现(垃圾回收器)

上一节:《JVM之GC算法》 知道GC算法的理论基础,咱们来看看具体的实现。只有落地的理论,才是真理。html

1、JVM垃圾回收器的结构

JVM虚拟机规范对垃圾收集器应该如何实现没有规定,由于没有最好的垃圾收集器,只有最适合的场景。算法

 

 图中展现了7种做用于不一样分代的收集器,若是两个收集器之间存在连线,则说明它们能够搭配使用。虚拟机所处的区域则表示它是属于新生代仍是老年代收集器。多线程

7种:serial收集器、parnew收集器、parallel scavenge收集器、serial  old 收集器、parallel old收集器、cms收集器、g1收集器(整堆收集器)、闭包

串行收集:单垃圾收集线程,进行收集工做,用户进程须要等待并发

并行收集:工做原理与串行同样,只是在收集垃圾时是多条线程同时进行,收集的效率在通常状况下天然高于单线程。性能

并发收集:指用户线程与垃圾收集线程同时工做(并发:同一时间间隔)。用户程序在继续运行,而垃圾收集程序运行在另外一个CPU上。spa

吞吐量:吞吐量就是CPU中用于运行用户代码的时间与CPU总消耗时间的比值(吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间))线程

一、Serial收集器
Serial(串行)收集器:最基本,最古老的收集器,只有一个线程进行垃圾收集器的工做,而且在进行垃圾收集工做时须要暂停其余工做线程(stop the word),直到他工做结束;
Serial收集器简单高效,工做时没有线程交互的开销,因此能够得到很高的单线程收集效率,对于运行在Client模式下的虚拟机来讲很适合。htm

"-XX:+UseSerialGC":添加该参数来显式的使用Serial垃圾收集器。对象

二、Serial Old收集器
  Serial Old收集器是Seria收集器的老年代版本,他一样是一个单线程收集器,使用" 标记-整理" 算法。
  Serial Old收集器主要用于Client模式下的虚拟机使用。
  Server模式下的两大用途:

    • 在JDK1.5及以前的版本与Parallel Scavenge收集器搭配使用;
    • 做为CMS收集器的后备方案,在并发收集发生Conturrent Mode Failure时使用。

三、ParNew 收集器
ParNew(并行)收集器就是Serial收集器的多线程版本,除了在收集垃圾时是启用多线程并行执行,其余行为(控制参数、收集算法、回收策略/Stop The Word、对象分配规则)彻底同样

 

 应用场景:ParNew收集器是许多运行在Server模式下的虚拟机中首选的新生代收集器,由于它是除了Serial收集器外,惟一一个能与CMS收集器配合工做的。

"-XX:+UseConcMarkSweepGC":指定使用CMS后,会默认使用ParNew做为新生代收集器。
"-XX:+UseParNewGC":强制指定使用ParNew。
"-XX:ParallelGCThreads":指定垃圾收集的线程数量,ParNew默认开启的收集线程与CPU的数量相同。

四、Parallel Scavenge收集器
Parallel Scavenge收集器 相似于 ParNew 收集器, Parallel Scavenge收集器 更加关注吞吐量(高效的CPU利用率)。CMS等垃圾收集器关注更多的是用户线程的停顿时间(提搞用户体验);Parallel Scavenge 收集器提供不少参数供咱们找到最合适的停顿时间或者最大吞吐量。JDK1.8 默认的方式;

Parallel Scavenge收集器提供了两个参数来用于精确控制吞吐量,一是控制最大垃圾收集停顿时间的 -XX:MaxGCPauseMillis参数,二是控制吞吐量大小的 -XX:GCTimeRatio参数;

  • “ -XX:MaxGCPauseMillis” 参数容许的值是一个大于0的毫秒数,收集器将尽量的保证内存垃圾回收花费的时间不超过设定的值(可是,并非越小越好,GC停顿时间缩短是以牺牲吞吐量和新生代空间来换取的,若是设置的值过小,将会致使频繁GC,这样虽然GC停顿时间下来了,可是吞吐量也下来了)。
  • “ -XX:GCTimeRatio”参数的值是一个大于0且小于100的整数,也就是垃圾收集时间占总时间的比率,默认值是99,就是容许最大1%(即1/(1+99))的垃圾收集时间。
  • “-XX:UseAdaptiveSizePolicy”参数是一个开发,若是这个参数打开以后,虚拟机会根据当前系统运行状况收集监控信息,动态调整新生代的比例、老年大大小等细节参数,以提供最合适的停顿时间或最大的吞吐量,这种调节方式称为GC自适应的调节策略。

应用场景:注重高吞吐量以及CPU资源敏感的场合,均可以优先考虑Parallel Scavenge+Parallel Old 收集器。

五、Paraller Old收集器
  Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。
  在JDK1.6中才出现。

六、CMS(Conturrent Mark Sweep)收集器

CMS收集器是一种以获取最短回收停顿时间为目标的收集器。CMS收集器是基于“标记-清除”算法实现,它的整个运行过程能够分为:

  • 初始标记:标记一下GC Roots能直接关联到的对象,这个过程速度很快,可是会暂停其余用户线程(Stop the word)
  • 并发标记:进行GCRoots Tracing的过程,同时开启GC和用户线程,用一个闭包的结构去记录可达对象,可是在这个阶段结束,该闭包不能保证其包含当前全部的可达对象。由于用户进程可能会不断的更新引用域,因此GC线程没法保证可达性分析的实时性。因此这个算法会跟踪记录这些发生引用更新的地方。
  • 从新标记:修正并发标记期间因用户线程继续运做而致使标记产生变更的那一部分对象的标记记录,该阶段会GC停顿,停顿时间比初始标记时间稍长,单远比并发标记时间短。
  • 并发清除:开启用户线程,同事GC线程清除死亡的对象

CMS收集器运行的整个过程当中,最耗费时间的是并发标记和并发清除,GC收集器线程和用户线程是一块儿工做的,因此整体来讲,CMS收集器的内存回收过程是与用户线程一块儿并发执行的。

优势:并发收集、低停顿。
缺点:

  • 一、CMS收集器对CPU资源很是敏感。虽然在两个并发阶段不会致使用户线程停顿,可是会由于占用了一部分线程而致使应用程序变慢,总吞吐量降低。CMS默认启动的回收线程数是(CPU数量+3)/4。
  • 二、:CMS收集器没法处理浮动垃圾,可能出现“Conturrent Mode Failure”失败而致使另外一次Full GC产生。因为CMS并发清除阶段用户线程还在运行,伴随着程序还在产生新的垃圾,这一部分垃圾出如今标记以后,CMS没法在当次收集中处理掉它们,只能留到下次再清理,这一部分垃圾称为“浮动垃圾”。也正是因为在垃圾收集阶段用户线程还在运行,那么也就须要预留有足够的内存空间给用户线程使用,所以CMS收集器不能像其余收集器那样等待老年代填满以后再进行收集,须要预留一部分空间给并发收集时用户程序使用。能够经过“-XX:CMSInitiatingOccupancyFraction”参数设置老年代内存使用达到多少时启动收集。
  • 三、:因为CMS收集器是一个基于“标记-清除”算法的收集器,那么意味着收集结束会产生大量碎片,有时候每每还有不少内存未使用,可是没有一块连续的空间来分配这个大对象,致使不得不提早触发一次Full GC。CMS收集器提供了一个“-XX:UseCMSCompactAtFullCollection”参数(默认是开启的)用于在CMS收集器顶不住要FullGC时开启内存碎片整理(内存碎片整理意味着没法并发执行不得不停顿用户线程)。参数“-XX:CMSFullGCsBeforeCompaction”来设置执行多少次不压缩的Full GC后,跟着来一次带压缩的(默认值是0,意味着每次进入Full GC时都进行碎片整理)。

七、G1(Garbage-First)收集器

G1的内存模型

G1收集器没有新生代和老年代的概念,而是将Java堆划分为一块块独立的大小相等的Region。当要进行垃圾收集时,首先估计每一个Region中的垃圾数量,每次都从垃圾回收价值最大的Region开始回收,所以能够得到最大的回收效率

Humongous是特殊的Old类型,专门放置大型对象.这样的划分方式意味着不须要一个连续的内存空间管理对象.G1将空间分为多个区域,优先回收垃圾最多的区域.
G1采用的是Mark-Copy ,有很是好的空间整合能力,不会产生大量的空间碎片
G1的一大优点在于可预测的停顿时间,可以尽量快地在指定时间内完成垃圾回收任务,在JDK11中,已经将G1设为默认垃圾回收器,经过jstat命令能够查看垃圾回收状况,在YGC时S0/S1并不会交换.

一个对象和它内部所引用的对象可能不在同一个Region中,那么当垃圾回收时,是否须要扫描整个堆内存才能完整地进行一次可达性分析?
固然不是,每一个Region都有一个Remembered Set,用于记录本区域中全部对象引用的对象所在的区域,从而在进行可达性分析时,只要在GC Roots中再加上Remembered Set便可防止对全部堆内存的遍历.

回收步骤

初始标记:标记与GC Roots直接关联的对象,中止全部用户线程,只启动一条初始标记线程,这个过程很快.
并发标记:进行全面的可达性分析,开启一条并发标记线程与用户线程并行执行.这个过程比较长.
最终标记:标记出并发标记过程当中用户线程新产生的垃圾.中止全部用户线程,并使用多条最终标记线程并行执行.
筛选回收:回收废弃的对象.此时也须要中止一切用户线程,并使用多条筛选回收线程并行执行.

G1为何能创建可预测的停顿时间模型?

由于它有计划的避免在整个Java堆中进行全区域的垃圾收集。G1跟踪各个Region里面的垃圾堆积的大小,在后台维护一个优先列表,每次根据容许的收集时间,优先回收价值最大的Region。这样就保证了在有限的时间内能够获取尽量高的收集效率。

G1与其余收集器的区别?

其余收集器的工做范围是整个新生代或者老年代、G1收集器的工做范围是整个Java堆。在使用G1收集器时,它将整个Java堆划分为多个大小相等的独立区域(Region)。虽然也保留了新生代、老年代的概念,但新生代和老年代再也不是相互隔离的,他们都是一部分Region(不须要连续)的集合。

2、如何选择垃圾收集器

一、单CPU或者小内存,单机程序 — -XX:+UseSerialGC
二、多CPU,须要大吞吐量,如后台计算型应用,容许工做线程停顿超过1秒  -XX:+UseParallelGC + -XX:+UseParallelOldGC
三、多CPU,追求低停顿时间,快速响应如互联网应用  -XX:+UseParNewGC + -XX:+UseConcMarkSweepGC

四、JVM本身选择

五、官方推荐G1,高性能

相关文章
相关标签/搜索