JVM-GC算法及垃圾收集器超级详细解读(520 是什么? 不存在!)

数哥:今天 05-20 据说这是一个节日?
我:不知道
数哥:那你今天怎么过?
我:撸起袖子-撸代码
JVM-GC算法及垃圾收集器超级详细解读(520 是什么? 不存在!)_第1张图片

一、GC算法

1、标记-清除算法

描述:

  1. 标记。从根结点(GC root)出发遍历对象,对访问过的对象打上标记,表示该对象可达。
  2. 清除。对那些没有标记的对象进行回收,这样使得不能利用的空间能够重新被利用。

优点

  • 实现简单
  • 不移动对象,

缺点:

  • 清理出来的空闲内存是不连续的
  • 效率比较低(递归与全堆对象遍历),而且在进行GC的时候,需要停止应用程序

JVM-GC算法及垃圾收集器超级详细解读(520 是什么? 不存在!)_第2张图片

2、标记-压缩算法(标记-整理)

描述:

  1. 从GCroot 开始遍历,标记活动的对象
  2. 清除没有被标记的空间
  3. 对空间碎片重排,变紧凑

优点:

  • 无内存碎片
  • 相对内存复制 不会浪费内存空间

缺点:

  • 压缩花费计算成本大

JVM-GC算法及垃圾收集器超级详细解读(520 是什么? 不存在!)_第3张图片

3、复制算法

描述:

  1. 在from区中,从GCroot 开始遍历,标记活动的对象
  2. 清除没有被标记的空间
  3. 将from区中 存活的对象空间 copy到 to 区
  4. 将from区和to区的 指针表示 swap

优点

  • 优秀的吞吐量
  • 可实现高速分配
  • 不会发生碎片化
  • 与缓存兼容

缺点:

  • 堆使用率低下
  • 不兼容保守式GC算法

JVM-GC算法及垃圾收集器超级详细解读(520 是什么? 不存在!)_第4张图片

4、分代收集算法

分代算法主要就是基于以上三种算法 ”因才施教“;

不同的区域使用 适合的算法;

具体的各种垃圾收集器 就是很好的实现

二、垃圾收集器

1.串行收集器

1)Serial收集器
特点:

​ 1、用于新生代

​ 2、使用复制算法

​ 3、单线程运行-进行垃圾收集时,暂停所有工作线程,即"Stop The World"

2)Serial-old
特点:

​ 1、用于老年代

​ 2、使用标记整理算法

​ 3、单线程运行-进行垃圾收集时,暂停所有工作线程,即"Stop The World"

时序图

JVM-GC算法及垃圾收集器超级详细解读(520 是什么? 不存在!)_第5张图片

设置:"-XX:+UseSerialGC":添加该参数来显式的使用串行垃圾收集器;

2. 并行收集器

1)ParNew

JVM-GC算法及垃圾收集器超级详细解读(520 是什么? 不存在!)_第6张图片

参数设置:

“-XX:+UseParNewGC”:强制指定使用ParNew;
“-XX:ParallelGCThreads”:指定垃圾收集的线程数量,ParNew默认开启的收集线程与CPU的数量相同;

2)Parallel收集器

JVM-GC算法及垃圾收集器超级详细解读(520 是什么? 不存在!)_第7张图片

Parallel Scavenge收集器类似ParNew收集器,Parallel收集器更关注系统的吞吐量;新生代使用"复制"算法,老年代使用"标记-压缩"

Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和"标记压缩"算法

参数设置:

-XX:+UseParallelGC 使用Parallel收集器+老年代串行

-XX:+UseParallelOldGC 使用Parallel收集器+老年代并行

3)CMS收集器(14版本 已经移除)

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器;

老年代收集器(新生代使用ParNew)老年代收集器(新生代使用ParNew);

初始标记、重新标记这两个步骤仍然需要"Stop The World";

JVM-GC算法及垃圾收集器超级详细解读(520 是什么? 不存在!)_第8张图片

优点:并发收集、低停顿
缺点:产生大量空间碎片、并发阶段会降低吞吐量

参数设置:

-XX:+UseConcMarkSweepGC 使用CMS收集器
-XX:+UseCMSCompactAtFullCollection "Full GC"后,进行一次碎片整理,整理过程是独占的,会引起停顿时间变长
-XX:+CMSFullGCsBeforeCompaction 设置进行几次Full GC后,进行一次碎片整理
-XX:ParallelCMSThreads 设定CMS的线程数量(一般情况约等于可用CPU数量)

4)G1收集器

G1收集器有以下特点:

  1. 空间整合,G1收集器采用"标记-压缩"算法,不会产生内存空间碎片。分配大对象时不会因为无法找到连续空间而提前触发下一次GC。
  2. 可预测停顿,这是G1的另一大优势,降低停顿时间是G1和CMS的共同关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为N毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎已经是实时Java(RTSJ)的垃圾收集器的特征了。

与其他垃圾收集器的区别:

上面提到的垃圾收集器,收集的范围都是整个新生代或者老年代;使用G1收集器时,Java堆的内存布局与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,新生代和老年代不再是物理隔阂,它们都是一部分(可以不连续)Region的集合

收集步骤:

  1. 标记阶段,首先初始标记(Initial-Mark),这个阶段是停顿的(Stop the World Event),并且会触发一次普通Mintor GC,对应GC log:GC pause (young) (inital-mark)
  2. Root Region Scanning,程序运行过程中会回收survivor区(存活到老年代),这一过程必须在young GC之前完成。
  3. Concurrent Marking,在整个堆中进行并发标记(和应用程序并发执行),此过程可能被young GC中断。在并发标记阶段,若发现区域对象中的所有对象都是垃圾,那个这个区域会被立即回收(图中打X)。同时,并发标记过程中,会计算每个区域的对象活性(区域中存活对象的比例)。
  4. Remark, 再标记,会有短暂停顿(STW)。再标记阶段是用来收集 并发标记阶段 产生新的垃圾(并发阶段和应用程序一同运行);G1中采用了比CMS更快的初始快照算法:snapshot-at-the-beginning (SATB)。
  5. Copy/Clean up,多线程清除失活对象,会有STW。G1将回收区域的存活对象拷贝到新区域,清除Remember Sets,并发清空回收区域并把它返回到空闲区域链表中。
  6. 复制/清除过程后。回收区域的活性对象已经被集中回收到深褐色和深绿色区域,未回收的部分保持原位置。

JVM-GC算法及垃圾收集器超级详细解读(520 是什么? 不存在!)_第9张图片

JVM-GC算法及垃圾收集器超级详细解读(520 是什么? 不存在!)_第10张图片

5)ZGC (11版本 新增)

在JDK 11当中,加入了实验性质的ZGC。它的回收耗时平均不到2毫秒。它是一款低停顿高并发的收集器。

ZGC几乎在所有地方并发执行的,除了初始标记的是STW的。所以停顿时间几乎就耗费在初始标记上,这部分的实际是非常少的。那么其他阶段是怎么做到可以并发执行的呢?

ZGC主要新增了两项技术,一个是着色指针Colored Pointer,另一个是读屏障Load Barrier

着色指针Colored Pointer
ZGC利用指针的64位中的几位表示Finalizable、Remapped、Marked1、Marked0(ZGC仅支持64位平台),以标记该指向内存的存储状态。相当于在对象的指针上标注了对象的信息。注意,这里的指针相当于Java术语当中的引用。

在这个被指向的内存发生变化的时候(内存在Compact被移动时),颜色就会发生变化。

在G1的时候就说到过,Compact阶段是需要STW,否则会影响用户线程执行。那么怎么解决这个问题呢?

读屏障Load Barrier 由于着色指针的存在,在程序运行时访问对象的时候,可以轻易知道对象在内存的存储状态(通过指针访问对象),若请求读的内存在被着色了。那么则会触发读屏障。读屏障会更新指针再返回结果,此过程有一定的耗费,从而达到与用户线程并发的效果。

常用的垃圾收集器组合

新生代 老年代
Serial Serial Old
Serial CMS + Serial Old
ParNew CMS
ParNew Serial Old
Parallel Scavenge Serial Old
Parallel Scavenge Parallel Old
G1 G1

个人水平有限,如有问题,欢迎大家留言指出,虚心接受,及时更正

如果大家觉得,还可以,烦请点赞收藏,谢谢

你可能感兴趣的:(JVM,算法,Java高级编程)