文章目录
垃圾收集器
多种垃圾收集器一般都是搭配使用,一般都是新生代垃圾收集器和老年代垃圾收集器搭配使用
1、Serial 收集器(新生代)
- 标记-复制算法
- 单线程收集器
- 必须暂停其他所有的工作线程
- 适合单核处理器或处理器核心数较少的环境
Serial
(串行)收集器是最基本、历史最悠久的垃圾收集器,采用==“标记-复制”==算法负责新生代的垃圾收集。它是Hotspot
虚拟机运行在客户端模式下的默认新生代收集器
它是一个==单线程收集器==。它会使用一条垃圾收集线程去完成垃圾收集工作,并且它在进行垃圾收集工作的时候,必须暂停其他所有的工作线程( “Stop The World” ),直到收集结束
这样的设计,带来的好处就是:简单高效。对于内存资源受限制的环境,它是所有收集器中额外内存消耗最小的收集器。适合单核处理器或处理器核心数较少的环境,每次收集几十MB
甚至一两百MB
的新生代内存,垃圾收集的停顿时间完全可以控制在十几毫秒或几十毫秒,最多一百多毫秒
2、Serial Old 收集器(老年代)
- 标记-整理算法
- 单线程收集器
Serial Old
收集器同样是一个单线程收集器,采用**“标记-整理”算法**负责老年代的垃圾收集,主要用于客户端模式下的HotSpot虚拟机使用
如果在服务器端使用,它主要有两种用途:
- 在
JDK5
及以前版本,与Parallel Scavenge
收集器搭配使用; - 作为
CMS
收集器发生失败时的后备预案;
3、ParNew 收集器(新生代)
- 标记-复制算法
- 多线程垃圾收集器
- 可以与
Serial Old
,CMS
垃圾收集器一起搭配工作
ParNew
收集器是一个多线程的垃圾收集器。它是运行在 Server
模式下的虚拟机的首要选择,可以与 Serial Old
,CMS
垃圾收集器一起搭配工作,采用**“标记-复制”算法**
4、Parallel Scavenge 收集器(新生代)
- 标记-复制算法
- 多线程收集器
- 目标则是达到一个可控制的吞吐量
- 停顿时间越短,就越适合需要与用户交互或需要保证服务响应质量的程序
Parallel Scavenge
收集器是也是一款新生代收集器,使用**“标记-复制”算法实现的多线程收集器**
Parallel Scavenge
收集器预其它收集器的目标不同,CMS
等其它收集器目标是尽可能缩短垃圾收集时用户线程的停顿时间。但是Parallel Scavenge
收集器的目标则是达到一个可控制的吞吐量。所谓吞吐量就是处理器用于运行用户代码的时间与处理器总消耗时间的比值
如果虚拟机完成某个任务,用户代码加上垃圾收集总共耗费了 100
分钟,其中垃圾收集花掉1
分钟,那吞吐量就是99%
。停顿时间越短,就越适合需要与用户交互或需要保证服务响应质量的程序,良好的响应速度能提升用户体验;而高吞吐量则可以最高效率地利用处理器资源,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的分析任务
5、Parallel Old 收集器(老年代)
- 标记-整理算法
- 多线程收集器
- 适用注重吞吐量或者处理器资源较为稀缺的应用场景
Parallel Old
收集器是一个多线程的垃圾收集器,使用**“标记-整理”算法**,是Parallel Scavenge
收集器的老年代版本。
在注重吞吐量或者处理器资源较为稀缺的应用场景,都可以优先考虑 Parallel Scavenge
收集器 + Parallel Old
收集器这个收集器组合
这个收集器是直到JDK6
时才开始提供的,在此之前,新生代的Parallel Scavenge
收集器一直处于相当尴尬的状态,原因是如果新生代选择了Parallel Scavenge
收集器,老年代除了Serial Old
收集器以外别无选择,其他表现良好的老年代收集器,如CMS
无法与它配合工作。由于老年代Serial Old
收集器在服务端应用性能上的“拖累”,使用Parallel Scavenge
收集器也未必能在整体上获得吞吐量最大化的效果。同样,由于单线程的老年代收集中无法充分利用服务器多处理器的并行处理能力,在老年代内存空间很大而且硬件规格比较高级的运行环境中,这种组合的总吞吐量甚至不一定比ParNew
加CMS
的组合来得优秀
6、CMS 收集器(老年代)
- 真正意义上的并发收集器
- 以获取最短回收停顿时间为目标
- 垃圾收集线程与用户线程(基本上)同时工作
CMS
(Concurrent Mark Sweep
·)收集器是一种以获取最短回收停顿时间为目标的收集器,**基于“标记-复制”算法实现,是 HotSpot 虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作
目前很大一部分的Java应用集中在互联网站点或者基于浏览器的B/S膝盖痛的服务器务端上,这类应用通常都会较为关注服务的响应速度,希望系统停顿时间尽可能短,以给用户带来自好的交互体验。所以,CMS收集器非常符合这类应用的收集场景
(1)工作流程
- 初始标记
(CMS initial mark)
:标记一下GC Roots
能直接关联到的对象,速度很快; - 并发标记
(CMS concurrent mark)
:从GC Roots
的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长,但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行; - 重新标记
(CMS remark)
:重新标记阶段,是为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段的时间长,远远比并发标记阶段时间短 - 并发清除
(CMS concurrent sweep)
:清理删除掉标记阶段判断的已经死亡的对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的
(2)优点和缺点
优点
并发收集、低停顿
缺点
- 影响用户线程的执行效率:并发标记和并发清除时,是和用户线程一起运行的,收集过程中肯定占用了用户程序的
CPU
资源。CMS
默认启动的回收线程数是(CPU数量+3)/4
,当CPU
数量在4
个以上时,垃圾回收线程占用不少于25%
的CPU
资源,势必影响用户线程的执行效率。 - 无法处理浮动垃圾:在并发清除阶段,用户线程并没有停止,所以还会继续产生新的垃圾,只能等待下一次收集时才能进行回收,这部分垃圾被称为“浮动垃圾”。
- 产生大量空间碎片:因为CMS收集器是基于“标记-清除”算法实现的,所以在进行大量的垃圾回收时,会产生很多不连续的内存空间。这是使用“标记-清除”算法都会有的缺点
由于垃圾收集阶段用户线程还需要持续运行,所以需要预留足够的内存空间提供给用户线程使用,因此CMS
收集器不能像其它收集器那样等到老年代几乎完全被填满了再进行收集。
- 在
JDK6
的默认设置中,CMS
收集器的启动阈值为92%
,代表老年代使用了92%
的空间后,就会启动CMS
收集器 - 如果
CMS
运行期间,无法满足程序分配新对象的需要,就会出现一次“并发失败”,这时候虚拟机将临时启动Serial Old
收集器进行老年代的垃圾收集
7、G1 收集器(老年代)
(1)什么是 G1 垃圾收集器
- 是面向服务器的垃圾收集器
- 主要针对配备多颗处理器、大容量内存的机器
G1
采用局部性收集的设计思路和基于Region
的内存布局形式
(2)G1 垃圾收集器的结构
G1
采用局部性收集的思想,对于堆空间的划分,采用Region
为单位的内存划分方式G1
垃圾回收器把堆划分成2048
个 大小相同的独立区域(Region)- 每个
Region
都会代表某一种角色- E: 代表
Eden
区 - S:代表
Survivor
区 - O:代表的是
Old
区 - H:代表的是
Humongous
(G1
用来分配大对象的区域,对于Humongous
也分配不下的超大对象,会分配在连续的N
个Humongous
中**)** - 灰色区域:代表的是空闲的
region
- E: 代表
G1可以面向堆内存任何部分来组成回收集来进行回收,衡量标准不再是它属于哪个分代,而是哪块内存存放的垃圾最多,回收收益最大,这就是G1收集器的 Mixed GC模式,即混合GC模式
(3)工作流程
-
初始标记(Initial Marking)
这个阶段仅仅只是标记GC Roots能直接关联到的对象,这阶段需要停顿线程,但是耗时很短
-
并发标记(Concurrent Marking)
从GC Roots开始对堆的对象进行可达性分析,递归扫描整个堆里的对象图,找出存活的对象,这阶段耗时较长,但是可以与用户程序并发执行
-
最终标记(Final Marking)
对用户线程做另一个短暂的暂停,用于处理并发阶段结束后遗留记录
-
筛选回收(Live Data Counting and Evacuation)
- 负责更新 Region 的统计数据,对各 Region 的回收价值和成本进行排序根据用户所期望的停顿时间来制定回收计划
- 可以自由选择多个
Region
来构成回收集 - 然后把回收的那一部分
Region
中的存活对象== >复制 ==>到空的Region中 - 最后对那些
Region
进行清空
(4)G1 垃圾收集器的特点
-
并行与并发
G1
能充分利用CPU
、多核环境下的硬件优势,使用多个CPU
(CPU
或者CPU
核心)来缩短Stop-The-World
停顿时间。部分其他收集器原本需要停顿Java
线程执行的GC
动作,G1
收集器仍然可以通过并发的方式让java
程序继续执行 -
分代收集
虽然
G1
可以不需要其他收集器配合就能独立管理整个GC
堆,但是还是保留了分代的概念。但它能够采用不同方式去处理新创建的对象和已存活一段时间、熬过多次GC
的旧对象来获取更好的收集效果 -
空间整合
G1
从整体来看是基于**“标记-整理”**算法实现的收集器- 从局部(两个
Region
之间)上来看是基于**“标记-复制”**算法实现的 - 这意味着
G1
运行期间不会产生内存空间碎片,收集后能提供规整的可用内存。此特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次GC
-
用户指定期望停顿
- 允许用户指定期望的停顿时间
- 设置不同的期望停顿时间,可以让
G1
在不同的场景下取得吞吐量和延迟之间的最佳平衡 G1
的默认停顿目标为200
毫秒,一般来说,设置为一百毫秒至两百毫秒这个区间都很正常- 如果期望停顿时间设置过短,会导致由于停顿目标时间太短,导致每次筛选出来的回收集只占堆内存很小的一部分,收集器的收集速度会跟不上分配速度,导致垃圾慢慢堆积
(5)G1 与 CMS 的区别
- 算法不同
CMS
采用“标记-清除”容易产生内存碎片,执行若干次GC
后进行1
次碎片整理G1
从整体来看是基于“标记-整理”算法实现的收集器,从局部(两个Region
之间)上来看是基于“标记-复制”算法实现
- 场景不同
- 小内存应用上
CMS
的表现大概率优于G1
- 大内存应用中,
G1
则能发挥优势 - 大小内存的参考值分水岭大概在
6GB-8GB
- 小内存应用上