亲宝软件园·资讯

展开

java垃圾收集器

​ 我犟不过你  人气:0

垃圾收集器如何演化的?

垃圾收集器的发展路线,简单来说是随着内存越来越大而发生变化。

从分代算法逐渐演化为不分代算法。

从serial的几十兆,逐渐演化到parallel的几个G,再到CMS的几十个G,也从此开始了并发回收。

年轻代收集器

Serial

特点:年轻代、串行回收、STW、简单高效

Serial(串行)收集器是最基本、发展历史最悠久的收集器,它是采用复制算法的新生代收集器,曾经(JDK 1.3.1之前)是虚拟机新生代收集的唯一选择。

在垃圾回收时,必须暂停其他所有线程的工作线程,即所谓的“Stop The World”。

Serial收集器是HotSpot虚拟机运行在Client模式下的默认新生代收集器。

Serial收集器具有简单而高效,由于没有线程交互的开销,可以获得最高的单线程收集效率(在单个CPU环境中)。

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

-XX:+UseSerialGC

ParNew

特点:年轻代、多线程回收、STW、配合CMS、核心越多效率越高

ParNew收集器是Serial收集器的多线程版本,除了使用多条线程进行垃圾收集之外,其余行为包括Serial收集器可用的所有控制参数、收集算法、Stop The Word、对象分配规则、回收策略等都与Serial收集器一样。

目前只有它能和CMS收集器(Concurrent Mark Sweep)配合工作。

在单CPU的环境下,其性能绝对不会有比Serial收集器有更好的效果,甚至由于多线程的切换开销,在双核情况下也有可能无法做到超越Serial。 在多核的情况下,GC线程数默认与核心数相同,随着核心的增多,能获得更好的效果。

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

-XX:+UseConcMarkSweepGC

强制指定使用ParNew

-XX:+UseParNewGC

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

-XX:ParallelGCThreads

Parallel Scavenge

特点:年轻代、并行回收、吞吐量、GC自适应的调节策略(GC Ergonomics)

Parallel Scavenge收集器是一个新生代收集器,使用复制算法,且是并行的多线程收集器。

Parallel Scavenge收集器关注点是达到一个可控制的吞吐量:

(吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间))

而其他收集器关注点在尽可能的缩短垃圾收集时用户线程的停顿时间。

控制最大垃圾收集停顿时间:参数允许的值是一个大于0的毫秒数,收集器将尽可能的保证内存垃圾回收花费的时间不超过设定的值(但是,并不是越小越好,GC停顿时间缩短是以牺牲吞吐量和新生代空间来换取的,如果设置的值太小,将会导致频繁GC,这样虽然GC停顿时间下来了,但是吞吐量也下来了)

-XX:MaxGCPauseMillis

控制吞吐量大小:参数的值是一个大于0且小于100的整数,也就是垃圾收集时间占总时间的比率,默认值是99,就是允许最大1%(即1/(1+99))的垃圾收集时间。

-XX:GCTimeRatio

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

-XX:+UseAdaptiveSizePolicy

老年代收集器

SerialOld

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

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

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

Server模式下的两大用途:

结合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个步骤:

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

三个缺点:

主要参数: 使用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的分区模型。

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

其实现过程如下图所示:

三色标记存在的问题 如下图所示,如果gc扫描到左侧位置,这时候程序执行C.d = D,B.d = null,则会导致B对D的引用消失,而C则引用了D,可是此时C是黑色的,不会对其子节点进行扫描,则D节点会被当做垃圾进行回收。

三色标记法问题解决方案

通常有两种解决方案:

增量更新(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的分类:

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本文会在后面主键补充:

加载全部内容

相关教程
猜你喜欢
用户评论