Java虚拟机 之 垃圾回收器

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/yichen97/article/details/91345311

引言

此篇是接着上一篇《垃圾回收算法》,也算是GC的下篇。

上一篇将 如何判定对象为垃圾的两种算法 和 垃圾回收的四种算法整理完毕了。由于适用的场景不同,垃圾收集器也是多种多样,这一篇准备介绍常用的几种垃圾回收器。

Serial 收集器

1. 首先,Serial收集器是最基本的、发展最悠久的垃圾收集器。

2. 它还是一个单线程垃圾收集器。假如有很多个线程正在运行,当需要垃圾回收的时候,所有线程全部暂停,然后垃圾回收线程开始执行。垃圾回收器执行完毕后,所有线程恢复,继续执行。

明明可以多线程执行,为什么垃圾收集器不与其他线程同时进行呢?

因为其他线程不停制造垃圾,垃圾回收器边清除垃圾,那垃圾将永远都打扫不完。就像妈妈在扫地,你在这边扔垃圾?

用途:在客户端(桌面应用)。因为客户端JVM所分配的内存非常小;又因为它是单线程的执行效率非常高,所以它收集垃圾的时间非常短,可以说基本上是无感知的。

ParNew 收集器

1. ParNew收集器是多个垃圾收集器同时进行的,性能得到提升,但在客户端还是不如Serial。

2. ParNew收集器与Serial收集器功能大部分都是相同的;且实现原理都是复制算法。

3. 在新生代内存必须使用Serial和ParNew。

Parallel Scavenge 收集器

1. 采用复制算法,主要回收新生代,所以为新生代收集器。

2. 多线程收集器。

3. 达到可控制的吞吐量(CPU用于运行代码的时间 与 CPU消耗的总时间的比值)。

吞吐量 = 执行用户代码时间 / (执行用户代码时间 + 垃圾回收时占用的时间)

那如何控制所说的吞吐量呢?这有两个参数可以控制。

-XX:MaxGCPauseMillis      //控制最大垃圾收集停顿时间

-XX:GCTimeRatio           //直接设置吞吐量大小(值的范围在(0,100))

既然最大的停顿时间可以控制,是不是最大停顿时间小了,性能就可以有提高呢?

不会的。该垃圾收集器是收集新生代内存的,垃圾回收器再进行垃圾区域划分的时候,停顿时间短的要比停顿时间长的新生代区域要小。

因为只有新生代区域变小了,垃圾收集器才能在规定的时间内完成回收。

并且,停顿时间短了,所带来的是垃圾收集变高。

Concurrent Mark Sweep (CMS) 收集器

1. 采用标记清除算法,所以它属于老年代收集器。

2. 属于并发收集器,但并不是完全的并发。

并发:回收垃圾和制造垃圾同时进行。

并行:回收垃圾和制造垃圾是分开的,但有多个同时进行。

了解了并发和并行,下面就介绍他的工作流程了。

它的主要工作流程包括:

1. 初始标记。标记GCRoot能直接关联到的对象。

2. 并发标记。顺着GCRoot能直接关联到的对象继续向下找。

3. 重新标记。对并发标记进行修正。

4. 并发清理。将标记的对象进行清除。

优点:并发收集;低停顿。

缺点:占用CPU资源高(因为并行执行);

无法处理浮动垃圾(刚清理完垃圾,立马又制造出垃圾,此种垃圾无法处理);

出现Concurrent Mode Failure(由于用户线程在创建对象,且标记或者清理仍在执行,所以它专门留出了一块区域,当有新的对象被创建,就往这块区域去存放。如果这块区域过大,就会浪费空间;如果过小,就会出现Concurrent Mode Failure错误。如果出现了这个错误就会触发一个Serial收集器来进行单线程回收,这个过程更加耗时);

有大量空间碎片(标记清除算法所导致)。

G1 收集器

内存并不是清晰的划分出新生代和老年代了,即使有,也是逻辑上的,不像以前直接物理上的隔离了。

G1收集器可算是之前所有收集器的优势整合,具体的优势有:

1. 并行:利用多核优势,缩短了停顿的时间;并发:提高速度。

2. 分代收集。将内存分成一块一块的Region,在后台存一张表(Remembered Set)来记录所有Region的回收情况。

3. 空间整合。采用类似于 标记-整理 算法,在进行大的对象分配的时候,就能找到大的内存,避免多次垃圾回收。

4. 可预测的停顿。可以让使用使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不超过N毫秒。

G1收集器的运作步骤的前三点与CMS收集器非常相似:

1. 初始标记;

2. 并发标记(并发);

3. 最终标记(并发);

4. 筛选回收(并发),假如想增加吞吐量,可以选择筛选回收这一过程为并行,并行执行可以提高垃圾回收效率。

Region之间对象引用以及其他收集器中的新生代和老年代之间的对象引用,如何避免全堆扫描?

使用Remembered Set避免全堆扫描。G1每个Region都有一个与之相对应的Remembered Set,虚拟机发现程序在进行对Reference类型的数据进行写操作的时候,会产生一个Write Barrier暂时中断写操作,检查Reference引用的对象是否处于不同的Region之中。

如果是,便通过CardTable把相关引用信息记录到被引用对象所属的Region的Remembered Set之中。当进行内存回收时,在GC根结点的枚举范围中加入Remembered Set即可保证不对全堆扫描也不会有遗漏。

猜你喜欢

转载自blog.csdn.net/yichen97/article/details/91345311