快捷搜索:  汽车  科技

jvm垃圾收集技巧:技术实验室-JVM详解垃圾收集器

jvm垃圾收集技巧:技术实验室-JVM详解垃圾收集器1.Serial收集器新生代并发 - Concurrent:即用户线程与垃圾线程同时执行所以并行和并发比较起来,大家就一目了然了。由于jvm虚拟机分代回收,按新生代和老生代划分进行比较和讲解。

上一节中讲到垃圾回收算法,jdk从1.2发展到现在经理的很多版本的变迁,由于Java不能像C 一样可以手动精准的回收对象,只能依靠jvm垃圾收集器,给java程序员带来便利的情况下就是牺牲了性能。作为一位java程序员一定要懂得每种垃圾收集器的特性和优势,选择恰当的垃圾收集器可以事倍功半,反之亦然。目前行业里最常见的就是一下七种垃圾回收器。如果两个收集器之间存在连接,就说明他们之间可以搭配使用

jvm垃圾收集技巧:技术实验室-JVM详解垃圾收集器(1)

新生代和老生代垃圾收集器

我们先认识下并行和并发的区别:

并行-Parallel:即多条垃圾收集线程并行工作,用户线程处于等待状态。如:ParNew、Parallel Scavenge、Parallel Old

jvm垃圾收集技巧:技术实验室-JVM详解垃圾收集器(2)

并发 - Concurrent:即用户线程与垃圾线程同时执行

jvm垃圾收集技巧:技术实验室-JVM详解垃圾收集器(3)

所以并行和并发比较起来,大家就一目了然了。

由于jvm虚拟机分代回收,按新生代和老生代划分进行比较和讲解。

新生代

1.Serial收集器

Serial收集器是最基本、发展历史最悠久的收集器,在JDK1.3.1之前,是虚拟机新生代收集的唯一选择。

特性:这个收集器是一个单线程的收集器,但是它的"单线程"的意义并不仅仅说明它只会使用一个CPU或者一条收集线程去完成垃圾收集工作,更重要的是它进行垃圾收集时,必须暂停其他所有的工作线程,直到收集结束。

优势:Serial收集器是虚拟机运行在Client模式下的默认的新生代收集器,java开发桌面程序,虚拟机就分为Client模式和Server模式,Server模式没有必要加载可视化界面,所以就有之前的Client。客户端程序可以加-client,但是JDK1.8之后被取消了。

简单、高效(与其它收集器的单线程比),对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。

2.ParNew收集器

ParNew收集器其实就是Serial收集器的多线程版本,除了使用多条线程进行垃圾收集外,其余与Serial收集器完全一样。ParNew收集器是许多运行在Server模式下的虚拟机中首选的新生代收集器。

Serial和ParNew的对比分析

与Serial收集器相比,ParNew在单CPU的环境下绝对不会有比Serial收集器更好的效果,甚至由于存在线程交互的开销,该收集器在通过超线程技术实现的两个CPU环境中都不能百分之百的保证可以超过Serial收集器。然而随着可以使用的CPU的数量的增加,它对于GC时系统资源的有效利用还是很有好处的。

jvm垃圾收集技巧:技术实验室-JVM详解垃圾收集器(4)

3.Parallel Scavenge收集器

Parallel Scavenge也是一个新生代收集器,使用复制算法并行收集器

Parallel Scavenge 收集器使用两个参数控制吞吐量

  • MaxGCPauseMillis:控制最大的垃圾收集停顿时间
  • GCRatio:直接设置吞吐量的大小

直观上,只要最大的垃圾收集停顿时间越小,吞吐量是越高的,但是GC停顿时间的缩短是以牺牲吞吐量和新生代空间作为代价的。比如原来10秒收集一次,每次停顿100毫秒。但是线程编程每5秒收集一次,每次停顿70毫秒,停顿时间下降的同时,吞吐量也下降了。

优势:Parallel Scavenge的自适应调节策略

Parallel Scavenge收集器有一个参数- XX: UseAdaptiveSizePolicy当这个参数打开之后,就不需要手动指定新生代的大小,Eden和Survivor区的比例,晋升老年代对象等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大吞吐量,这种调节方式成为GC自适应的调节策略。

Parallel Scavenge和ParNew的对比分析

停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验,而高吞吐量则可以高效地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。他们的主要区别就是Parallel Scavenge的自适应调节策略。

jvm垃圾收集技巧:技术实验室-JVM详解垃圾收集器(5)

老年代

4.CMS收集器

CMS 采用的是标记 - 清除算法,并且是并发的。除了少数几个操作需要 Stop-the-world 之外,它可以在应用程序运行过程中进行垃圾回收。在并发收集失败的情况下,Java 虚拟机会使用其他两个压缩型垃圾回收器进行一次垃圾回收。由于 G1 的出现,CMS 在 Java 9 中已被废弃。

特性:由于垃圾回收时,都需要暂停用户线程,CMS(Concurrent Mark Sweep)收集器是一种以 获取最短停顿时间 为目标的收集器,重视服务的响应速度,希望系统停顿时间最短,能给用户带来良好的体验。CMS收集器是基于"标记-清除"算法实现的,它的运作过程比较复杂,整个过程分为四个步骤:

  • 初始标记 初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,需要Stop The World(暂停所有的用户线程)
  • 并发标记 并发标记阶段就是进行GC Roots Tracing的过程 (用户不暂停)—用户不暂停就还可能产生一些对象与GC Roots不可达
  • 重新标记重新标记阶段是为了修正 并发标记期间 因用户程序继续运作而导致标记产生变动 的那一部分对象的标记记录,这个阶段的停顿时间会比初始阶段稍长一些 但是远比并发标记的时间短,仍然需要"Stop The World"
  • 并发清除并发清除阶段会清除对象**(用户不暂停)**

整个过程中耗时最长的并发表及和并发清除过程收集线程可以与用户线程一起工作,所以整体上来说,CMS收集器的内存回收过程与用户线程一起并发执行。

优势:并发、低停顿

缺点:

  • CMS收集器对CPU的资源非常敏感,在并发阶段,它虽然不会导致用户线程停顿,但是会因为占了一部分CPU资源,而导致应用程序变慢,总吞吐量会降低。
  • CMS无法处理浮动垃圾,由于CMS 并发清理阶段用户线程还在运行着,用户线程在运行自然就还会有新的垃圾产生,CMS无法在当次收集中处理掉它们,只好留到下一次GC再清理掉,这一部分垃圾叫做"浮动垃圾"。也是由于在垃圾收集阶段用户线程还需要运行,那也就还需要预留有足够的内存空间给用户线程使用,因此CMS不能等到老年代几乎满了才开始收集,需要预留一部分空间提供给并发时的程序运作使用。要是CMS在运行期间预留的内存无法满足程序需要,就会出现一次"Concurrent Mode Failure",这时虚拟机将启动后备方案:临时启用Serial Old收集器来重新进行老年代的垃圾收集,这样下来停顿时间就长了。
  • CMS收集器会产生大量的空间碎片,CMS是一款基于"标记-清除" 算法实现的收集器,这意味着收集结束时会有大量空间碎片产生。空间碎片过多时,就会给大对象的分配带来很多麻烦,往往会出现还有很大的空间剩余,但是无法找到足够大连续的空间来分配当前对象,不得不提前触发一次Full GC。

jvm垃圾收集技巧:技术实验室-JVM详解垃圾收集器(6)

Parallel Scavenge收集器 VS CMS收集器

CMS关注点是尽可能的缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量,由于与吞吐量密切相关,Parallel Scavenge收集器也经常称为"吞吐量优先"收集器

5.Serial Old收集器

Serial Old是Serial收集器的老年代版本,同样是单线程收集器,使用标记-整理算法。

jvm垃圾收集技巧:技术实验室-JVM详解垃圾收集器(7)

6.Parallel Old收集器

特性:Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程标记-整理算法。

在注意吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge加 Parallel Old收集器

这个收集器是在JDK1.6中才开始提供的,在此之前Parallel Scavenge一直处于尴尬的状态。原因是如果新生代选择了Parallel Scavenge收集器,老年代除了Serial Old别无选择,由于老年代Serial Old性能上的拖累,使用了Parallel Scavenge收集器也未必能在整体应用上获得吞吐量的最大化效果,直到Parallel Old收集器出现后,"吞吐量优先"收集器终于有了名副其实的应用组合

  • JDK1.6之前 Parallel Scavenge Serial Old
  • JDK1.6以及之后 Parallel Scavenge Parallel Old

jvm垃圾收集技巧:技术实验室-JVM详解垃圾收集器(8)

7.G1收集器

又叫全区域的垃圾回收器。是在JDK 7u4版本之后发布的垃圾收集器,并在jdk9中成为默认垃圾收集器。通过“-XX: UseG1GC”启动参数即可指定使用G1 GC。G1 能够针对每个细分的区域来进行垃圾回收。在选择进行垃圾回收的区域时,它会优先回收死亡对象较多的区域。这也是 G1 名字的由来。

G1垃圾回收器在清除实例所占用的内存后,还会做内存压缩。G1垃圾回收器回收Region的时候基本不会Stop The World,从整体来看是基于标记-整理算法,从局部(两个region之间)来看基于复制算法。

因为G1中的垃圾收集区域是“分区”(Region)的。G1的分代收集和以上垃圾收集器不同的就是除了有年轻代的ygc,全堆扫描的fullgc外,还有包含所有年轻代以及部分老年代Region的MixedGC。G1的优势还有可以通过调整参数,指定垃圾收集的最大允许pause time。

G1分区的概念

G1将堆分成了若干Region 以下和”分区”代表同一概念。Region的大小可以通过G1HeapRegionSize参数进行设置,其必须是2的幂,范围允许为1Mb到32Mb。 JVM的会基于堆内存的初始值和最大值的平均数计算分区的尺寸,平均的堆尺寸会分出约2000个Region。分区大小一旦设置,则启动之后不会再变化。如下图简单画了下G1分区模型。

jvm垃圾收集技巧:技术实验室-JVM详解垃圾收集器(9)

  • Eden regions(年轻代-Eden区)
  • Survivor regions(年轻代-Survivor区)
  • Old regions(老年代)
  • Humongous regions(巨型对象区域)是指,占用了Region容量的50%以上的一个对象。Humongous区,就专门用来存储巨型对象。如果一个H区装不下一个巨型对象,则会通过连续的若干H分区来存储。因为巨型对象的转移会影响GC效率,所以并发标记阶段发现巨型对象不再存活时,会将其直接回收。ygc也会在某些情况下对巨型对象进行回收
  • Free resgions(未分配区域,也会叫做可用分区)-上图中空白的区域

G1类似CMS,也会在比如一次fullgc中基于堆尺寸的计算重新调整(增加)堆的空间。但是相较于执行fullgc,G1 GC会在无法分配对象或者巨型对象无法获得连续分区来分配空间时,优先尝试扩展堆空间来获得更多的可用分区。原则上就是G1会计算执行GC的时间,并且极力减少花在GC上的时间(包括ygc mixgc) 如果可能,会通过不断扩展堆空间来满足对象分配、转移的需要。

因为G1提供了“可预测的暂停时间”,也是基于G1的启发式算法,所以G1会估算年轻代需要多少分区,以及还有多少分区要被回收。ygc触发的契机就是在Eden分区数量达到上限时。一次ygc会回收所有的Eden和survivor区。其中存活的对象会被转移到另一个新的survivor区或者old区,如果转移的目标分区满了,会再将可用区标记成S或者O区。

下一节将讲解jvm调优内容,请小伙伴们继续关注。

猜您喜欢: