8.8种GC及其基本原理介绍

概述

相关文章在此总结如下:

文章地址
jvm基本知识地址
jvm类加载系统地址
双亲委派模型与打破双亲委派地址
运行时数据区地址
运行时数据区-字符串常量池、程序计数器、直接内存地址
jvm中对象创建流程与内存分配地址
jvm对象内存布局地址

垃圾回收

垃圾回收是必须的,不然 jvm 内存很快就会满。
没有被引用的对象,称之为垃圾对象

垃圾对象查找方式

  • 引用计数法 (Reference Counting)
  • 根可达分析算法 (GCRooting Tracing)

引用计数法 (Reference Counting)

当对象引用消失,对象就称为垃圾

堆内存中主要存在三种引用关系:

  • 单一引用
  • 循环引用
  • 无引用
    在这里插入图片描述
    由上图可知,引用计数法,是无法发现 循环引用 这种情况的,易发生内存泄露问题。性能也是有问题的,要全部遍历一次,这是这种计数法的缺陷。

根可达分析算法 (GCRooting Tracing)

通过 GCRoots作为对象起点向下搜索,当一个对象到 GCRoots 没有任何引用链时此对象是垃圾
在这里插入图片描述

引用链 (ReferenceChain) : GCRoots 搜索走过的路径

垃圾对象死亡前至少经历两次标记:

  • 第一次:可达性分析,没有引用链对象会被第一次标记
  • 第二次:标记后的对象会经历筛选,如果筛选不通过,则会被第二次标记

对象引用类型

对象引用有哪些?

jdk 1.2之后,java对象的引用进行了扩充:强引用软引用、弱引用、虚引用

引用类型被垃圾回收时间用途生存时间
强引用从来不会对象的一般状态jvm停止时终止
软引用内存不足时对象缓存内存不足时终止
弱引用正常GC对象缓存GC后终止
虚引用正常GC类似事件回调机制GC后终止
无引用正常GC对象的一般状态GC后终止

强引用

代码中普遍的存在,只要强引用还在,就不会被GC。

Object obj = new Object();

软引用

非必须引用,内存溢出之前进行回收,如内存还不够,才会抛出异常。

package com.fun.info;

import java.lang.ref.SoftReference;

public class Reference {
    public static void main(String[] args) {
        Object obj = new Object();
        SoftReference<Object> sf = new SoftReference<>(obj);
        obj = null;
        // 有时候会返回 null
        Object o = sf.get();
        System.out.println("o==" + o);
    }
}

应用场景:软引用可用来实现内存敏感的高速缓存。
例如:

应用需要读取大量本地文件,如果每次读取都从硬盘读取会严重影响性能,如果一次性全部加载到内存,内存可能会溢出
可以使用软引用解决这个问题,使用一个HashMap来保存文件路径和文件对象管理的软引用之间的映射关系。
内存不足时,jvm会自动回收缓存文件对象的占用空间,有效避免 OOM 问题

HashMap<String, SoftReference<InputStream>> fileCache = new HashMap<>();

弱引用

非必须引用,只要有GC,就会被回收。

 Object o1 = new Object();
 WeakReference<Object> wf = new WeakReference<>(o1);
 o1 = null;
 // 有时候会返回 null
 Object o2 = wf.get();
 // 返回是否被垃圾回收器标记为即将回收的垃圾
 boolean enqueued = wf.isEnqueued();
 System.out.println("o1 = " + o2);
 System.out.println("enqueued =" + enqueued);
  • 虚引用是每次垃圾回收的时候都会被回收,通过虚引用的get方法永远获取到的数据为null,因此也被称为幽灵引用
  • 作用:跟踪对象被垃圾回收的状态,仅仅是提供一种确保对象被回收后,做某些事情的机制。类似监听机制。
  • ThreadLocalThreadLocalMap 就是用了弱引用。

清除垃圾

1.标记-清除算法 (Mark-Sweep)

  • 分为 标记清除 两个阶段:
    • 标记:标记出所有需要回收对象
    • 清除:统一回收掉所有对象
  • 缺点:
    • 执行效率不稳定
    • 空间碎片:会产生大量不连续内存碎片

2.复制算法 (Copying)

  • 内存分为两块,清除垃圾时,将存活对象复制到另一块
  • S0和S1区就是基于这个算法诞生的
  • Eden:S = 8:2
  • 不用担心S区不够,由Old做内存担保
  • 优缺点:
    • 优点:没有内存空间碎片化
    • 缺点:存在空间浪费

3.标记-整理算法 (Mark-Compact)

  • 标记:标记出所有需要回收对象
  • 清除:统一回收掉所有对象
  • 整理:将所有存活对象向一端移动
  • 优缺点:
    • 优点:空间没有浪费,没有内存碎片化问题
    • 缺点:性能较低

分代回收 (Generational Collection)

  • 新生代:选择 复制算法弱分代假说
  • 老年代:选择 标记-清除标记-整理,强分代假说

垃圾回收器

有 8 种不同的垃圾回收器,它们分别用于不同分代的垃圾回收

新生代 (复制算法) : Serial 、ParNew 、Parallel Scavenge
老年代 (标记-清除、标记-整理):SerialOld、Parallel Old、CMS
整堆:G1、ZGC

查看jdk使用哪个gc

Last login: Mon Nov 20 08:27:51 2023 from 10.35.232.22
[root@hadoop01 ~]# /data/jdk-11.0.20/bin/java  -XX:+PrintCommandLineFlags -version
-XX:G1ConcRefinementThreads=43 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=2098669376 -XX:MaxHeapSize=32178700288 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC 
java version "11.0.20" 2023-07-18 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.20+9-LTS-256)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.20+9-LTS-256, mixed mode)

GC-串行收集器

Serial与SerialOld

配置参数:-XX:+UseSerialGC
特点:

  • Serial新生代收集器,单线程执行,使用复制算法
  • SerialOld老年代收集器,单线程执行,使用标记-整理算法
  • 进行垃圾收集时,必须暂停用户线程

SafePoint
垃圾回收时,是需要暂停用户线程的,如果产生垃圾的速度大于回收的速度,那永远回收不了。
SafePoint线程挂起的点主要有:

  • 循环的末尾
  • 方法返回前
  • 调用方法的call之后
  • 抛出异常的位置

并行收集器

Parallel Scavenge (Stop)

配置参数: -XX:+UseParallelGC
特点:简称 PS

  • 吞吐量优先收集器,垃圾收集需要暂停用户线程
  • 新生代使用并行回收器,采用复制算法
  • 老年代使用串行收集器,采用标记-整理算法

Parallel Old

配置参数: -XX:+UseParallelOldGC
特点:

  • PS收集器的老年代版本
  • 吞吐量优先收集器,垃圾收集需要暂停用户线程,对 CPU 敏感
  • 新生代使用复制算法
  • 老年代使用并行收集器,采用 标记-整理算法

ParNew

配置参数: -XX:+UseParNewGC
配置参数: -XX:ParallelGCThreads=n、垃圾收集线程数
特点:

  • 新生代并行ParNew,老年代串行SerialOld
  • Serial的多线程片
  • 单核CPU不建议使用

CMS

配置参数: -XX:+UseConcMarkSweepGC
特点:

  • 低延时,减少STW(Stop-The-World)对用户的影响
  • 并发收集,用户线程与收集线程一起执行,对CPU资源敏感
  • 不会等待堆填满再收集,到达阀值就开始收集
  • 采用 标记-清除算法 ,所以会产生内存碎片

实际上是精细了标记阶段的流程,降低 STW 来实现低延时

  • 初始标记阶段:会STW,标记出GCRoots可以关联到的对象 ,关联对象较少,所以很快
  • 并发标记阶段:不会STW,遍历GCRoots直接对象的引用链,耗时长
  • 重新标记阶段:会STW,修正并发标记期间的新对象记录
  • 并发清除阶段:不会STW,清除垃圾对象,释放内存空间

Garbage-First (G1)

G1是一款面向服务端应用的全功能垃圾收集器大内存 企业配置的主要是G1。
配置参数: -XX:+UseG1GC
特点

  • 吞吐量和低延时都行的整堆垃圾收集器
  • G1最大堆内存 32M2048 = 64GB,最小堆内存 1M2048=2GB,低于此值不建议使用
  • 全局使用标记-整理算法收集,局部采用复制算法收集
  • 可预测的停顿:能让使用者指定GC消耗时间(超过这个时间,则判断无回收价值,不会回收,即筛选回收),默认是200ms。

详细流程:

  • 初始标记:会STW,标记出GCRoots可以关联到的对象,耗时短
  • 并发标记:不会STW,遍历GCRoots直接对象的引用链,耗时长
  • 最终标记:会STW,修正并发标记期间,标记产生变动的那部分
  • 筛选回收:会STW,对各个Region的回收价值成本排序,根据用户期望GC停顿时间确定回收计划
G1内存划分

在这里插入图片描述
**取消新生代与老年代的物理划分:**采用若干个固定大小的Region

Region区类型(逻辑上):

  • Eden区
  • Survivor
  • Old
  • Humongous 当对象的容量超过了Region的50%,则被认为是巨型对象
# 使用G1垃圾收集器
-XX:+UseG1GC
# 设置期望达到的最大GC停顿时间指标(jvm会尽力实现,但不保证达到),默认值是 200 毫秒
-XX:MaxGCPauseMillis=
# 设置的 G1 区域的大小。值是 2 的幂,范围是 1MB 到 32MB 之间
# 目标是根据最小的 java 堆大小划分出约 2048 个区域
# 默认是堆内存的 1/2000
-XX:G1HeapRegionSize=n
# 设置并行垃圾回收线程数,一般将n的值设置为逻辑处理器的数量,建议最多为8.
-XX:ParallelGCThreads=n
# 设置并行标记的线程数。将n设置为ParallelGCThreads的1/4左右。
-XX:ConcGCThreads=n
# 设置触发标记周期的 java 堆占用率阀值。默认占用率是整个 java 堆的 45%
-XX:InitiatingHeapOccupancyPercent=n

ZGC

ZGC (Z Garbage Collector) 在 jdk11 中引入 的一种可扩展的低延迟垃圾收集器,在jdk15中发布稳定版本

配置参数: -XX:+UseZGC
特点:

  • <1ms最大暂停时间(jdk16是10ms,jdk16+是<1ms),不会随着堆内存增加而增加
  • 适合内存8MB,16TB
  • 并发,基于Region、压缩、NUMA感知、使用色彩指针、使用负载屏障
  • 垃圾收集算法:标记-整理算法
  • 主要目标:低延时

相关参数:

# 启用ZGC
-XX:+UseZGC
# 设置最大堆内存
-Xmx
# 打印 GC 日志
-Xlog:gc
# 打印 GC 详细日志
-Xlog:gc*

结束

至此,GC基本原理就结束了,如有疑问,欢迎评论区留言。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流月up

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值