经典垃圾收集器
Serial收集器(新生代)
Serial 即串行的意思,也就是说它以串行的方式执行,它是**单线程的收集器,只会使用一个线程进行垃圾收集工作,GC 线程工作时,其它所有线程都将停止工作。是最基础,历史最悠久**的收集器。
使用复制算法收集新生代垃圾。
它的优点是简单高效,在单个 CPU 环境下,由于没有线程交互的开销,因此拥有最高的单线程收集效率,所以,它依然是HotSpot在 Client 场景下的默认新生代收集器。
ParNew收集器(新生代)
就是 Serial 收集器的多线程版本,但要注意一点,ParNew 在单核环境下是不如 Serial 的,在多核的条件下才有优势。
使用复制算法收集新生代垃圾。
HotSpot在Server 场景下默认的新生代收集器,除了性能原因外,主要是因为除了 Serial 收集器,只有它能与 CMS 收集器配合使用。
Parallel Scavenge收集器(新生代)
同样是多线程的收集器,其它收集器目标是尽可能缩短垃圾收集时用户线程的停顿时间,而它的目标是提高吞吐量(吞吐量 = 运行用户程序的时间 / (运行用户程序的时间 + 垃圾收集的时间))。
停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验。而高吞吐量则可以高效率地利用 CPU 时间,尽快完成程序的运算任务,适合在后台运算而不需要太多交互的任务。
使用复制算法收集新生代垃圾。
Serial Old收集器(老年代)
Serial 收集器的老年代版本,单线程,HotSpot在Client 场景下默认的老年代垃圾收集器。
使用标记-整理算法收集老年代垃圾。
用处有如下2个:
- 在JDK5以及之前的版本中与Parallel Scavenge收集器搭配使用
- 作为CMS收集器发生失败时的后备预案,在并发收集发生Concurrent Mode Failure时使用
Parallel Old收集器(老年代)
Parallel Scavenge 收集器的老年代版本,多线程
在注重吞吐量的场景下,可以采用 Parallel Scavenge + Parallel Old 的组合。
使用标记-整理算法收集老年代垃圾。
CMS收集器(老年代)
它划时代的意义就在于垃圾回收线程几乎能做到与用户线程同时工作,多线程
使用**标记-清除算法**收集老年代垃圾。
运作过程分为4个步骤
- 初始标记(CMS initial mark)(STW)
标记一下GC Roots能直接关联到的对象,速度很快
时间很短
- 并发标记(CMS concurrent mark)
从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集一起并发运行
时间长
- 重新标记(CMS remark)(STW)
为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录
时间较短
- 并发清除(CMS concurrent sweep)
清理删除掉标记阶段判断的已经死亡的对象,由于不需要移动存活对象,所以看这个阶段也是可以与用户线程同时并发的
时间长
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XirmQZLn-1630463397518)(C:\Users\吴著敲66\Pictures\CMS收集器.png)]
但 CMS 收集器也有如下缺点:
- 吞吐量低(需要更多的CPU资源)
- 无法处理浮动垃圾
- 标记 - 清除算法带来的内存空间碎片问题
由于在GC过程中用户线程在持续运行,那就需要预留足够的内存空间供用户线程使用,因此CMS收集器不能等到老年代几乎完全被填满了再进行收集,必须预留空间供并发收集时的程序运作使用。
如果预留空间不够,会出现一次“并发失败”(Concurrent Mode Failure),这个时候虚拟机会启用后备预案:冻结用户线程的执行,临时启用Serial Old收集器来重新进行老年代的垃圾收集。
G1收集器(全堆)
G1 (Garbage-First)是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器. 以极高概率满足GC停顿时间要求的同时,还具备高吞吐量性能特征.
G1的堆内存结构
堆内存被划分为固定大小的多个Region区域.
每个Region都可以根据需要,扮演新生代的Eden空间、Survivor空间或者老年代空间,这些区域都扮演者都是动态变化的。
收集器能够对扮演不同角色的Region采用不同的策略去处理
每个Region区的大小在JVM启动时就确定了. JVM 通常生成 2000 个左右的heap区, 根据堆内存的总大小,区的size范围允许为 1Mb 到 32Mb且为2的N次幂。
对象大小超过了一个Region大小的一半称为大对象,在Humongous区域中存储;如果超过了一整个Region,则会被存放在N个连续的Humongous Region中,G1大多数情况将其看做为老年代的一部分。
G1的预测功能
G1可以面向堆内存任何部分组成回收集(Collection Set)进行回收,衡量标准是哪块内存中存放的垃圾数量最多,回收效益最大。之所以可达到预测,原因是它能将Region作为垃圾回收的最小单元,每次回收的内存空间都是Region大小的整数倍。
G1收集器的停顿预测模型是以衰减均值为理论基础来实现,在垃圾回收过程中,G1收集器会记录每个Region的回收耗时、每个Region记忆集里的脏卡数量等各个可测量不步骤所花费的成本。
衰减均值是指会比普通平均值更容易受到新数据的影响,这个值更准确地代表“最近的”平均状态
Region的统计状态越新越能决定其回收的价值,然后通过这些信息预测回收,由哪些Region组成回收集才可以在不超过期望停顿时间的约束下获得更高的收益。
G1的跨引用问题
G1的记忆集存储本质是哈希表,Key是其他Region的起始地址,Value是一个集合,里面存储的是卡表索引号,这种“双向”的卡表结构更复杂,并且Region区域很多,每个区域都有一个记忆集,G1至少耗费相对于Java堆容量10%~20%额外内存维持工作。
G1垃圾回收时分配内存
类似于本地内存线程缓冲(TLAB),每一个Region中有两个名为TAMS(Top at Mark Start)指针,垃圾回收过程中把Region中一部分空间划分出来用于并发回收中新对象的分配,这些新对象的地址必须在这两个指针位置上。
G1收集器大致运作过程:
- 初始标记( Initial Marking):
仅仅只是标记一下GC Roots 能直接关联到的对象,并且修改TAMS指针的值,让下一阶段用户线程并发运行时,能正确地在可用的Region中分配新对象。这个阶段需要停顿线程,但耗时很短,而且是借用进行MinorGC的时候同步完成的,所以G1收集器在这个阶段实际并没有额外的停顿。 - 并发标记( Concurrent Marking):
从GC Root开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行。当对象图扫描完成以后,还要重新处理SATB记录下的在并发时有引用变动的对象。 - 最终标记(Final Marking):
对用户线程做另一个短暂的暂停,用于处理并发阶段结束后遗留的最后少量SATB记录 - 筛选回收(Live Data Couting and Evcatio):
负责更新Region的统计数据,对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可以自由选择任意多个Region构成回收集,然后把决定回收的那一部分Region的存活对象复制到空的Region中,再清理掉整个旧Region的全部空间。这里的操作涉及存活对象的移动,是必须暂停用户线程,由多条收集器线程并行完成的。
总结
收集器 | 收集对象和算法 | 收集器类型 | 说明 | 适用场景 |
Serial | 新生代,复制算法 | 单线程 | 简单高效;适合内存不大的情况 | |
ParNew | 新生代,复制算法 | 并行的多线程收集器 | ParNew垃圾收集器是Serial收集器的多线程版本 | 搭配CMS垃圾回收器的首选 |
Parallel Scavenge | 新生代,复制算法 | 并行的多线程收集器 | 类似ParNew,更加关注吞吐量,达到一个可控制的吞吐量 | 本身是Server级别多CPU机器上的默认GC方式,主要适合后台运算不需要太多交互的任务 |
Serial Old | 老年代,标记整理算法 | 单线程 | Client模式下虚拟机使用 | |
Parallel Old | 老年代,标记整理算法 | 并行的多线程收集器 | Paraller Scavenge收集器的老年代版本,为了配置Parallel Svavenge的面向吞吐量的特性而开发的对应组合 | 在注重吞吐量以及CPU资源敏感的场合采用 |
CMS | 老年代,标记清除算法 | 并行与并发收集器 | 尽可能的缩短垃圾收集时用户线程停止时间;缺点在于,1.内存碎片,2.需要更多CPU资源,3.浮动垃圾问题,需要更大的堆空间 | 重视服务的相应速度,系统停顿时间和用户体验的互联网网站或者B/S系统。互联网后端目前cms是主流的垃圾回收器 |
G1 | 跨新生代和老年代;标记整理+化整为零 | 并行与并发收集器 | JDK1.7才正式引入,采用分区回收的思维,基本不牺牲吞吐量的前提下完成低停顿的内存回收;可预测的停顿是其最大的优势 |