简单的梳理JVM(四)——GC回收回收机制

概述

       垃圾收集 Garbage Collection 通常被称为“GC”,它诞生于1960年 MIT 的 Lisp 语言,经过半个多世纪,目前已经十分成熟了。

        JVM中,程序计数器、虚拟机栈、本地方法栈都是随线程而生随线程而灭,栈帧随着方法的进入和退出做入栈和出栈操作,实现了自动的内存清理,因此,我们的内存垃圾回收主要集中于 java 堆和方法区中,在程序运行期间,这部分内存的分配和使用都是动态的。


一、GC回收流程

  • 对内存中的对象判断是否有存活
  • 通过收集器对非存活的对象,进行GC清理。

二、对象存活判断

1.引用计数

判断方式:

  • 每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。

存在问题:

  • 此方法简单,无法解决对象相互循环引用的问题。

2.可达性分析

判断流程:

  • 从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。不可达对象。

3.举个栗子

场景:

A 对象中引用了B对象,而B对象引用了C对象,C 中有引用了D对象,D中引用了B对象。

引用计数
当A对象没有引用时,清理了A对象,而B,C,D循环引用,则无法判断清理。

可达性分析
整个调用链就是 A——>B——>C——>D——>B 。整个路径的GC Root 就是A ,当A失效时,GC就会同时清理掉A,B,C,D 。

4.GC Roots包括:

  • 虚拟机栈中引用的对象。
  • 方法区中类静态属性实体引用的对象。
  • 方法区中常量引用的对象。
  • 本地方法栈中JNI引用的对象。
  • 简单来说,也就是当对象出现为以上情况时,可以认为对象是存活的,不进行清理回收。否则,需要调用GC进行回收。

三、GC垃圾回收算法

1.标记 -清除算法

  1. 对内存中的对象进行可行分析,对判断可达的对象标记。
  2. 完成标记后,对未标记的(非可达对象)清理。

存在问题:

  • 标记和清除的效率不高。
  • 标记清除后,hui会产生很多不连续的空间碎片,导致后续分配大内存对象时,需要再次触发GC回收动作。

在这里插入图片描述

2.复制算法

  1. 把内存按照容量划分为大小相等的两块。
  2. 每次只使用其中一块,在快要使用完之前,把当前这块中存活的对象复制到另一块中.
  3. 然后把当前这块全部清理掉。

特点:

  • 简单高效

主要问题:

  • 内存减半,以及会对长期存活的对象,来回复制,降低效率。

在这里插入图片描述

3.标记压缩算法

  1. 对内存中对象进行标记,但不会立马处理。
  2. 把标记的对象移动到内存某一端。
  3. 清理边界之外的内存。

优点:

  • 解决标记-清理算法的问题(标记清除后,hui会产生很多不连续的空间碎片,导致无法分配大内存对象)

在这里插入图片描述

四、GC收集器

1.Serial (-XX:+UseSerialGC)

算法使用:

  • 新生代复制算法
  • 老年代标记压缩算法

实现流程:

  1. 单线程去收集不可达的对象
  2. 停止所有工作线程
  3. 清理不可达对象
  4. 恢复程序正常执行

在这里插入图片描述

2.ParNew(-XX:+UseParNewGC)

算法使用:

  • 新生代复制算法
  • 老年代标记压缩算法

实现流程:

  1. 多线程收集不可达对象
  2. 停止所有工作线程
  3. 多线程清理不可达对象
  4. 恢复正常工作线程

参数控制:

  • -XX:+UseParNewGC ParNew收集器
  • -XX:ParallelGCThreads 限制线程数量

在这里插入图片描述

3.Parallel Scavenge(-XX:+UseParallelGC)

  • 与ParNew收集器类似,但提供了参数可以自适应的调节策略,提高收集器的吞吐量。虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或最大的吞吐量;

算法使用:

  • 新生代复制算法
  • 老年代标记压缩算法

实现流程:

  1. 多线程收集不可达对象
  2. 自适应的调节策略,选择合适的时间,停止所有工作线程
  3. 多线程清理不可达对象
  4. 恢复正常工作线程

参数控制:

  • -XX:+UseParallelGC
  • -XX:MaxGCPauseMillis 垃圾回收器最大停顿时间
  • -XX:GCTimeRatio 吞吐量大小(0,100)默认最大99 吞吐量:(用户执行时间)/(GC暂停时间+用户执行时间)

4.Serial Old(-XX:+UseSerialOldGC)

  • Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。这个收集器是在JDK
    1.6中才开始提供

参数控制:

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

5.CMS(重点)(-XX:+UseConcMarkSweepGC)

一种以获取最短回收停顿时间为目的的收集器。

过程步骤:

  1. 初始标记:仅仅标记GC ROOT能直接关联到的对象,对象往下关联的对象是不标记的。特点:会有STW,速度很快。
  2. 并发标记:与用户线程并行执行,对被GC ROOT直接标记的对象,向下进行标记处理。特点:无STW停顿,并发执行。
  3. 重新标记:修正在并发标记期间,因用户程序执行运作导致的产生变动的一部分需要标记清理对象,进行标记。特点:会有STW,但时间不长
  4. 并发清除:并行用户线程,执行回收处理。特点:无STW停顿,并发执行。

在这里插入图片描述

优点:

  • 并发收集、低停顿

缺点:

  • 产生大量空间碎片、并发阶段会降低吞吐量

参数控制:

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

Parallel Old(-XX:+UseParallelOldGC)

  • ​ Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用"标记-整理"算法,多核,自适应调整。
  • 这个收集器是在JDK1.6版本中出现的,所以在JDK1.6之前,新生代的Parallel Scavenge只能和Serial Old这款单线程的老年代收集器配合使用。
  • Parallel Old垃圾收集器和Parallel Scavenge收集器一样,也是一款关注吞吐量的垃圾收集器,和Parallel Scavenge收集器一起配合,可以实现对Java堆内存的吞吐量优先的垃圾收集策略。

G1

  • 这东西有内容些多,以后新开一篇,仔细理一理。

总结

本章内容主要内容:

  1. 理清楚GC回收的流程以及判断依据
  2. 弄懂GC的三种回收算法
  3. 弄明白每个收集器使用了那些算法,以及各自的优缺点。
原文地址:https://www.cnblogs.com/one-reader/p/14100085.html