jvm-第四节垃圾回收器的细节实现

发布时间 2023-06-27 17:54:46作者: 小傻孩丶儿

# 垃圾回收器串讲及 HostSpot 的细节实现

本篇知识点概况

  1. 并发标记与三色标记
  2. gc并发下漏标问题与不同垃圾回收期下的处理方案(G1,Cms对比)
  3. 跨代引用
  4. 安全点与安全区域
  5. gc参数(了解)
  6. 其他的垃圾回收期(了解)

并发标记与三色标记

  1. 三色标记诞生的历史:在三色标记之前有一个标记清除算法,根据可达性,可达设置为1,不可达设置为0,都完事之后统一清理,但是不能异步,所以stw时间较长,对于要求实时性的系统不可接受便有了可以异步的三色标记

  2. 三色标记的概念:三色分别是黑 灰 白,支持并发

    1. 黑色:跟对象,且它所有的引用都已经扫描
    2. 灰色:本身被扫描,但引用的对象没有扫描完
    3. 白色:未被扫描
    4. 1687852455810

gc并发下漏标问题与不同垃圾回收期下的处理方案(G1,Cms对比)

  1. 三色标记漏标问题:原因是并发扫描过程中,引用发生变化,以下面三张图为例
    1. 下面的图讲述扫描时引用发生了变化
    2. 1687852628344
    3. 1687852636960
    4. 1687852644727
  2. 如何解决这个漏标问题,下面分别是cms与g1中解决方法
    1. cms解决漏标之incremental update:当一个白色对象被一个黑色对象引用,将黑色标记为灰色,重新扫描;
    2. g1解决漏标之satb:stab既快照,当一个对象被修改后,会进行标记,然后颜色改成灰色,并在下次回收的时候进行处理,标记为黑色

跨代引用

  1. 跨代引用的问题:堆分为新生代,老年代,如果老年代对象引用了新生代对象,新生代回收就要扫描整个老年代,开销太大,
  2. 解决方法1-记忆集:rset,相当于一个位图记录,新生代,老年代之间的引用关系,可以避免扫描整个老年代
  3. 解决方法2-cardTable:是一种记录堆内存区域是否被修改的数据结构,按页划分表格,记录被修改的页,垃圾回收时只扫描dirty的页,避免全盘扫描

安全点与安全区域

  1. 安全点作用:所有线程进入安全点,用户线程暂停,gc线程开始工作

  2. 什么是安全点:比如方法调用,循环跳转,异常跳转,一般这些指令才会产生安全点,用户线程在执行过程中会不断轮训这个安全点,如果发现为true就在最近的安全点上主动挂起

  3. 安全区域的作用:所有线程进入安全点,用户线程暂停,gc线程开始工作;代码在这段区域中对象引用关系不会发生变化;其次是如果线程一直sleep,block,程序是无法进入安全点的,

  4. 什么是安全区域:举个例子 sychronized

  5. private static Selector selector;
    
    private static void processRequests() {
        // 进入安全区域
        synchronized(selector) {
            // 在安全区域内进行 I/O 操作
            selector.select();
            // 处理 I/O 事件
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
            while(keyIterator.hasNext()) {
                SelectionKey key = keyIterator.next();
                if(key.isAcceptable()) {
                    // 处理连接请求
                } else if (key.isReadable()) {
                    // 处理读事件
                } else if (key.isWritable()) {
                    // 处理写事件
                }
                keyIterator.remove();
            }
        }
        // 离开安全区域,进行其他计算
        // ...
    }
    
    

gc参数(了解)

  1. 1687858783195

  2. 上面是一条gc日志

  3. gc常用参数

  4. GC 常用参数
    -Xmn -Xms -Xmx –Xss 年轻代 最小堆 最大堆 栈空间
    -XX:+UseTLAB 使用 TLAB,默认打开
    -XX:+PrintTLAB 打印 TLAB 的使用情况
    -XX:TLABSize 设置 TLAB 大小
    -XX:+DisableExplicitGC 启用用于禁用对的调用处理的选项 System.gc() -XX:+PrintGC 查看 GC 基本信息
    -XX:+PrintGCDetails 查看 GC 详细信息
    -XX:+PrintHeapAtGC 每次一次 GC 后,都打印堆信息
    -XX:+PrintGCTimeStamps 启用在每个 GC 上打印时间戳的功能
    -XX:+PrintGCApplicationConcurrentTime 打印应用程序时间(低) -XX:+PrintGCApplicationStoppedTime 打印暂停时长(低)
    -XX:+PrintReferenceGC 记录回收了多少种不同引用类型的引用(重要性低)
    -verbose:class 类加载详细过程
    -XX:+PrintVMOptions 可在程序运行时,打印虚拟机接受到的命令行显示参数
    -XX:+PrintFlagsFinal -XX:+PrintFlagsInitial 打印所有的 JVM 参数、查看所有 JVM 参数启动的初始值(必须会用)
    -XX:MaxTenuringThreshold 升代年龄,最大值 15, 并行(吞吐量)收集器的默认值为 15,而 CMS 收集器的默认值为 6。 Parallel 常用参数
    -XX:SurvivorRatio 设置伊甸园空间大小与幸存者空间大小之间的比率。默认情况下,此选项设置为 8
    -XX:PreTenureSizeThreshold 大对象到底多大,大于这个值的参数直接在老年代分配
    -XX:MaxTenuringThreshold 升代年龄,最大值 15, 并行(吞吐量)收集器的默认值为 15,而 CMS 收集器的默认值为 6。
    -XX:+ParallelGCThreads 并行收集器的线程数,同样适用于 CMS,一般设为和 CPU 核数相同
    -XX:+UseAdaptiveSizePolicy 自动选择各区大小比例
    CMS 常用参数
    -XX:+UseConcMarkSweepGC 启用 CMS 垃圾回收器
    -XX:+ParallelGCThreads 并行收集器的线程数,同样适用于 CMS,一般设为和 CPU 核数相同
    

其他的垃圾回收器(了解)

  1. 传统的垃圾回收期:内存占用,吞吐量,延时只能满足俩,而 现在延时这个指标越来越重要,就有了低延迟垃圾回收器

  2. Eplison(了解即可)
    这个垃圾回收器不能进行垃圾回收,是一个“不干活”的垃圾回收器,由 RedHat 退出,它还要负责堆的管理与布局、对象的分配、与解释器
    的协作、与编译器的协作、与监控子系统协作等职责,主要用于需要剥离垃圾收集器影响的性能测试和压力测试。
    ZGC(了解即可)
    有类似于 G1 的 Region,但是没有分代。
    标志性的设计是染色指针 ColoredPointers(这个概念了解即可),染色指针有 4TB 的内存限制,但是效率极高,它是一种将少量额外的信息存储在指针上
    的技术。
    它可以做到几乎整个收集过程全程可并发,短暂的 STW 也只与 GC Roots 大小相关而与堆空间内存大小无关,因此考科一实现任何堆空间 STW 的时间小于
    十毫秒的目标。
    Shenandoah(了解即可)
    第一款非 Oracle 公司开发的垃圾回收器,有类似于 G1 的 Region,但是没有分代。
    也用到了染色指针 ColoredPointers。
    效率没有 ZGC 高,大概几十毫秒的目标
    

问题

  1. 垃圾回收器 从cms到g1发展中的变化,比如针对漏标部分的处理,针对跨代引用的处理;

  2. 安全点和安全区域的区别