java常见的垃圾回收器,如何理解java的垃圾收集机制

  java常见的垃圾回收器,如何理解java的垃圾收集机制

  00-1010垃圾收集器是如何进化的?较年轻的收集器SerialParNewParallel Scavenge,以及较老的收集器Serial oldparallellodcms(ConcurrentMarksweep),新的收集器G1

  00-1010垃圾收集器的开发路线,简单来说就是随着内存的增大而变化。

  逐渐从分代算法演变到非分代算法。

  从几十兆的串行,逐渐演变到几个G的并行,再到几十个G的CMS,然后开始并发回收。

  

目录

 

  00-1010特点:年轻一代,连环回收,STW,简单高效

  串行(Serial)采集器是最基础的采集器,发展历史最长。它是使用复制算法的新一代收集器,并且是(在JDK 1.3.1之前)新一代虚拟机集合的唯一选择。

  在垃圾收集期间,所有其他线程的工作线程都必须挂起,即所谓的“停止世界”。

  串行收集器是在客户端模式下运行的热点虚拟机的默认新一代收集器。

  串行采集器简单高效。因为没有线程交互的开销,所以可以获得最高的单线程收集效率(在单CPU环境下)。

  添加该参数来显式的使用Serial垃圾收集器:

  -XX:使用串行GC

  00-1010特点:年轻一代,多线程回收,STW,与CMS合作,内核越多,效率越高。

  PAR收集器是串行收集器的多线程版本。除了使用多线程进行垃圾收集,其他行为包括所有控制参数、收集算法、停止字、对象分配规则、收集策略等。适用于串行收集器的选项与串行收集器的选项相同。

  目前,只有它可以与CMS收集器(并发标记清除)一起工作。

  在单CPU的环境下,它的性能永远不会比串行采集器好。即使因为多线程的切换开销,在双核的情况下也不一定能超越串行。在多核的情况下,默认情况下,GC线程的数量与内核的数量相同。随着芯数的增加,可以获得更好的结果。

  指定使用CMS后,会默认使用ParNew作为新生代收集器:

  -xx: useconcmarkswepgc强制指定使用ParNew。

  -XX: UseParNewGC指定垃圾收集的线程数量,ParNew默认开启的收集线程与CPU的数量相同:

  -xx:并行线程

  00-1010特点:年轻一代,平行回收,处理量,气相色谱适应性调整策略(气相色谱人体工程学)

  并行清除收集器是新一代收集器,采用复制算法,是一种并行多线程收集器。

  并行清除收集器的重点是实现:的可控吞吐量。

  (吞吐量=运行用户代码的时间/(运行用户代码的时间/垃圾收集时间)),而其他的收集器则侧重于尽可能缩短用户线程在垃圾收集过程中的暂停时间。

  控制最大垃圾收集停顿时间:该参数的允许值是大于0的毫秒,收集器会尽力保证内存垃圾收集时间不超过设定值(不过,越小越好,GC暂停时间越短是以牺牲吞吐量和新生成空间为代价的。如果设置的值太小,会导致频繁的GC,这样虽然GC暂停时间下降,但吞吐量也会下降)

  -xx 3360 maxgcpausemillis控制吞吐量大小:该参数的值是一个大于0小于100的整数,即垃圾收集时间与总时间的比值。默认值为99,即允许的最大垃圾收集时间为1%(即1/(1 99))。

  -xx : gctime ratioGC自适应的调节策略(GC Ergonomics):

  当这个参数打开之后,就不需要手工指定新生代的大小、Eden与Survivor区的比例、晋升老年代对象年龄等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量,这种调节方式称为GC自适应的调节策略(GC Ergonomics)。只需要把基本的内存数据设置好(如-Xmx设置最大堆),然后使用MaxGVPauseMillis参数或GCTimeRation参数给虚拟机设立一个优化目标,JVM会自动调节其他优化参数

  

-XX:+UseAdaptiveSizePolicy

 

  

老年代收集器

 

  

SerialOld

特点:老年代、串行回收、STW

 

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

  Serial Old收集器主要用于Client模式下的虚拟机使用。

  Server模式下的两大用途:

  在JDK1.5及之前的版本与Parallel Scavenge收集器搭配使用;作为CMS收集器的后备方案,在并发收集发生Conturrent Mode Failure时使用。结合Serial的回收流程如下图所示:

  

 

  

 

  

ParallelOld

特点:老年代、多线程、STW、吞吐量

 

  Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多线程和标记-整理算法。此收集器的出现代表着吞吐量优先收集器终于有了比较名副其实的应用组合,在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge加Parallel Old收集器。

  使用po收集参数:

  

-XX:+UseParallelOldGC

与Parallele Scavenge结合使用的回收流程如下:

 

  

 

  使用吞吐量收集器的吞吐量计算方式如下图所示:

  

 

  

 

  

CMS(ConcurrentMarkSweep)

特点:老年代、并发的,短停顿(降低STW时间)

 

  CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,它非常符合那些集中在互联网站或者B/S系统的服务端上的Java应用,这些应用都非常重视服务的响应速度。从名字上(Mark Sweep)就可以看出它是基于标记-清除算法实现的。

  CMS问题比较多,所以现在没有一个版本默认是CMS,只能手工指定。

  CMS既然是MarkSweep,就一定会有碎片化的问题,碎片到达一定程度,CMS的老年代分配对象分配不下的时候,使用SerialOld 进行老年代回收。

  CMS的回收过程主要分为4个步骤:

  初始标记标记一下GC Roots能直接关联到的对象,速度很快,需要Stop The World。并发标记进行GC Roots Tracing(根可达算法)的过程,在整个过程中耗时最长,标记所有根节点能照的对象。与用户线程一起工作。重新标记为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。此阶段也需要Stop The World。并发清理与用户线程一起运行。

 

  从以上过程中可以总结出其优点:并发收集,并发清理,地停顿。

  三个缺点:

  CMS收集器对CPU资源非常敏感。 CMS默认启动的回收线程数是(CPU数量+3)/4,回收线程会占用部分线程资源,导致系统吞吐量降低。在选用CMS收集器的时候,需要考虑,当前的应用系统,是否对CPU资源敏感。垃圾收集过程中,无法处理浮动垃圾,可能会出现Concurrent Mode Failure问题,导致触发Full GC。浮动垃圾:是由于CMS收集器的并发清理阶段,清理线程是和用户线程一起运行,如果在清理过程中,用户线程产生了垃圾对象,由于过了标记阶段,所以这些垃圾对象就成为了浮动垃圾。CMS无法在当前垃圾收集过程中集中处理这些浮动垃圾,需要预留内存空间去保存这些垃圾,可以通过参数 -XX:CMSInitiatingOccupancyFraction 控制触发占用老年代的百分比。如果预留空间无法装下浮动垃圾,会导致Concurrent Mode Failure失败,这个时候,虚拟机将启动后备预案,临时启动Serial Old收集器来对老年代重新进行垃圾收集,这样会导致垃圾收集的时间边长,特别是当老年代内存很大的时候。所以对参数-XX:CMSInitiatingOccupancyFraction的设置,过高,会导致发生Concurrent Mode Failure,过低,则浪费内存空间。使用的"标记-清除"算法会出现很多内存碎片。 过多的内存碎片导致缺少连续的内存空间,会影响大对象的分配,最终触发Full GC,为了解决这个问题,CMS收集器提供了一个"-XX:+UseCMSCompactAtFullCollection"参数(默认是开启的),用于CMS收集器在必要的时候对内存碎片进行压缩整理。由于内存碎片整理过程不是并发的,所以会导致停顿时间变长。虚拟机还提供了一个-XX:CMSFullGCsBeforeCompaction"参数,来控制进行过多少次不压缩的Full GC以后,进行一次带压缩的Full GC,默认值是0,表示每次在进行Full GC前都进行碎片整理。主要参数: 使用CMS收集器:

  

-XX:+UseConcMarkSweepGC

Full GC后,进行一次碎片整理;整理过程是独占的,会引起停顿时间变长:

 

  

-XX:+UseCMSCompactAtFullCollection

设置进行几次Full GC后,进行一次碎片整理:

 

  

-XX:+CMSFullGCsBeforeCompaction

设定CMS的线程数量(一般情况约等于可用CPU数量):

 

  

-XX:ParallelCMSThreads

 

  

新型收集器

 

  

G1

算法:三色标记+SATB

 

  G1(Garbage-First)收集器是当今收集器技术发展最前沿的成果之一,它是一款面向服务端应用的垃圾收集器,HotSpot开发团队赋予它的使命是(在比较长期的)未来可以替换掉JDK 1.5中发布的CMS收集器。G1从JDK9开始已经作为默认的垃圾回收器。如果对于应用程序来说停顿时间比吞吐量更重要,G1是非常合适的选择。

  G1与上面介绍的GC有很大的不同

  G1的设计原则是首先收集尽可能多的垃圾(Garbage First)。 G1并不会等内存耗尽(串行、并行)或者快耗尽(CMS)的时候开始垃圾收集,而是在内部采用了启发式算法,在老年代找出具有高收集收益的分区进行收集。G1采用内存分区(Region)的思路逻辑上的分代概念,不是物理分代所有收集都是STW,混合(mixed)收集(年轻代和老年代同时进行收集)三色标记法说起并发回收,就不得不了解三色标记法。先学习三色标记法,更好的理解G1的分区模型。

  我们将对象分成三种类型:

  黑色:跟对象或者该对象与它的子对象都被扫描(标记完成)。灰色:对象本身被标记完成,但是还没有扫描完该对象中的子对象白色:未被扫描的对象。或者是扫描完成后,最终白色对象为不可达对象即为垃圾对象。其实现过程如下图所示:

  

 

  第一步:根对象被置为黑色,子节点置为灰色。第二步:继续遍历灰色对象,将遍历过的子节点置为灰色,当前灰节点置为黑色。第三部:遍历所有可达对象后,所有可达对象置为黑色,不可达的为白色,将要被回收。三色标记存在的问题如下图所示,如果gc扫描到左侧位置,这时候程序执行C.d = D,B.d = null,则会导致B对D的引用消失,而C则引用了D,可是此时C是黑色的,不会对其子节点进行扫描,则D节点会被当做垃圾进行回收。

  

 

  三色标记法问题解决方案

  通常有两种解决方案:

  在插入的时候记录对象(CMS增量更新)在删除的时候记录对象(G1 STAB)增量更新(Incremental update): 在CMS采用的是增量更新(Incremental update),只要在写屏障(write barrier)里发现要有一个白对象的引用被赋值到一个黑对象 的字段里,那就把这个白对象变成灰色的。即插入的时候记录下来。

  STAB(snapshot-at-the-beginning): STAB 的做法在 GC 开始时,在初始标记时对内存进行一个对象图的逻辑快照 (snapshot),只要被快照到对象是活的,那在整个 GC 的过程中对象就被认定的是活的,即使该对象的引用稍后被修改或者删除。

  同时新分配的对象也会被认为是活的,除此之外其它不可达的对象就被认为是死掉了。这样 STAB 就保证了真正存活的对象不会被 GC 误回收,但同时也造成了某些可以被回收的对象逃过了 GC,导致了内存里面存在浮动的垃圾 (float garbage),这些浮动垃圾将在下次回收时被收集。

  G1的分区

  G1是逻辑分代,将堆分为若干个区域(region),新生代的垃圾收集依然采用暂停所有应用线程的方式,将存活对象拷贝到老年代或者Survivor空间。老年代也分成很多区域,G1收集器通过将对象从一个区域复制到另外一个区域,完成了清理工作。这样就解决了CMS中的内存碎片问题。

  

 

  如上图分代模型所示,G1仍然有Eden,Survivor,Old等区域,同时多了一个Humongous Region(巨型对象)。

  巨型对象:一个大小达到甚至超过分区大小一半的对象称为巨型对象(Humongous Object)。

  巨型对象:会独占一个、或多个连续分区,其中第一个分区被标记为开始巨型(StartsHumongous),相邻连续分区被标记为连续巨型(ContinuesHumongous)。巨型对象会直接在老年代分配,所占用的连续空间称为巨型分区(Humongous Region)。G1内部做了一个优化,一旦发现没有引用指向巨型对象,则可直接在年轻代收集周期中被回收。

  G1中GC的分类:

  Young GC:主要是对Eden区进行GC,它在Eden空间耗尽时会被触发。 在这种情况下,Eden空间的数据移动到Survivor空间中,如果Survivor空间不够,Eden空间的部分数据会直接晋升到年老代空间。Survivor区的数据移动到新的Survivor区中,也有部分数据晋升到老年代空间中。最终Eden空间的数据为空,GC停止工作,应用线程继续执行Mix GC:不仅进行正常的新生代垃圾收集,同时也回收部分后台扫描线程标记的老年代分区。G1是否有Full GC?答案是肯定的,G1触发了Full GC,这时G1会退化使用Serial Old收集器来完成垃圾的清理工作。

  Full GC产生的原因?G1在对象复制/转移失败或者没法分配足够内存(比如巨型对象没有足够的连续分区分配)时,会触发Full GC。Full GC使用的是stop the world的单线程的Serial Old模式,所以一旦触发Full GC则会STW应用线程,并且执行效率很慢。JDK 8版本的G1是不提供Full GC的处理的。对于G1 GC的优化,很大的目标就是没有Full GC。

  剩下三种GC本文会在后面主键补充:

  ZGC (10ms - 1ms) PK C++:算法:ColoredPointers + LoadBarrierShenandoah:算法:ColoredPointers + WriteBarrierEplison:Epsilon不回收内存,只负责堆的管理与布局,对象的分配,与解释器、编译器、监控子系统的协作; epsilon适合运行时间短、在内存耗尽前就可退出的应用程序到此这篇关于学习java一定要知道的垃圾收集器的文章就介绍到这了,更多相关java垃圾收集器内容请搜索盛行IT以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: