Home
  • 计算机网络
  • 操作系统
  • 数据结构与算法
  • 设计模式
  • JavaSE
  • JVM
  • JUC
  • Netty
  • CPP
  • QT
  • UE
  • Go
  • Gin
  • Gorm
  • HTML
  • CSS
  • JavaScript
  • vue2
  • TypeScript
  • vue3
  • react
  • Spring
  • SpringMVC
  • Mybatis
  • SpringBoot
  • SpringSecurity
  • SpringCloud
  • Mysql
  • Redis
  • 消息中间件
  • RPC
  • 分布式锁
  • 分布式事务
  • 个人博客
  • 弹幕视频平台
  • API网关
  • 售票系统
  • 消息推送平台
  • SaaS短链接系统
  • Linux
  • Docker
  • Git
GitHub (opens new window)
Home
  • 计算机网络
  • 操作系统
  • 数据结构与算法
  • 设计模式
  • JavaSE
  • JVM
  • JUC
  • Netty
  • CPP
  • QT
  • UE
  • Go
  • Gin
  • Gorm
  • HTML
  • CSS
  • JavaScript
  • vue2
  • TypeScript
  • vue3
  • react
  • Spring
  • SpringMVC
  • Mybatis
  • SpringBoot
  • SpringSecurity
  • SpringCloud
  • Mysql
  • Redis
  • 消息中间件
  • RPC
  • 分布式锁
  • 分布式事务
  • 个人博客
  • 弹幕视频平台
  • API网关
  • 售票系统
  • 消息推送平台
  • SaaS短链接系统
  • Linux
  • Docker
  • Git
GitHub (opens new window)
  • JVM内存模型
  • 类加载
  • 垃圾回收
    • 死亡对象判断
    • 哪些对象可以作为GC ROOTS?
    • 垃圾回收算法
    • 三色标记法
    • 写屏障
    • CMS
    • G1
    • ZGC
  • 调优与问题排查
  • JVM
Nreal
2023-11-01
目录

垃圾回收

# 死亡对象判断

  • 引用计数法

    无法解决循环引用问题

  • 可达性分析

    GC ROOTS对象作为起点搜索引用链,不在引用链上的对象为垃圾;

    如何判断废弃常量?

    JDK1.7之后,字符串常量池在堆中,如果没有任何String对象引用该字符串,就说明该常量为废弃常量;

    如何判断无用类?

    1. 所有类实例被回收;
    2. 加载该类的ClassLoader被回收;
    3. 该类对应的Class对象不被引用,不被反射访问;

    满足以上条件,“可以回收”,并非立即回收;

# 哪些对象可以作为GC ROOTS?

  • 栈中引用的对象
  • 方法区类静态属性引用的对象;
  • 方法区常量引用的对象;

# 垃圾回收算法

  • 标记清除

    大量不连续内存碎片

  • 复制算法

    可用内存缩小为一半,不适用老年代(对象多,复制性能差)

  • 标记整理

    适用于老年代回收频率不高的场景

  • 分代收集

    将堆分为新生代和老年代,新生代每次回收有大量对象死去,选择标记复制算法,老年代选择标记清除/整理算法;

    对象何时从新生代进入老年代?

    长期存活的对象,对象在Survivor中每熬过一次MinorGC,年龄就+1,默认15,就可晋升到老年代;

    可以通过参数 -XX:MaxTenuringThreshold 来设置;

# 三色标记法

参考文章:JVM 垃圾回收算法详解(CMS、三色标记) (opens new window)

并发标记阶段由于用户线程和GC线程同时运行,会造成已经标记过的对象引用发生变化,会产生多标,漏标问题,漏标采用三色标记法解决;

三色标记算法 把Gc Roots 可达性分析算法过程中遍历的对象,按照“是否访问过”这个条件标记成以下三种颜色:

黑色:表示对象已经被垃圾收集器访问过并且从这个对象出发的多有引用都已经标记过。 如果一个新的标量指向一个黑色对象是不需要重新扫描一遍。黑色对象不可能直接指向白色对象(跳过灰色对象);

灰色:表示对象已经被垃圾收集器访问过 但是至少存在1条以上的引用未被扫描过;

白色:表示未被扫描的对象。显然在可达性分析刚刚开始的阶段, 所有的对象都是白色的, 若在分析结束的阶段, 仍然是白色的对象, 即代表不可达;

准备扫表B→D 之前,此时用户线程 删除了B-D 的引用,增加了A-D 的引用,此时根据可达性分析是无法到达D, 显然D又不是垃圾对象,如果将D 按垃圾对象清除,就是JVM一个非常严重的bug;

# 写屏障

所谓写屏障就是在复制前后增加一些操作(类似以AOP概念)

void oop_field_store(oop* field, oop new_value) {  
    pre_write_barrier(field);          // 写屏障-写前操作
    *field = new_value; 
    post_write_barrier(field, value);  // 写屏障-写后操作
}
1
2
3
4
5

写屏障实现SATB

G1

当对象B的成员变量的引用发生变化时,比如引用消失(a.b.d = null),我们可以利用写屏障,将B原来成员变量的引用对象D记录下来:

void pre_write_barrier(oop* field) {
    oop old_value = *field;    // 获取旧值
    remark_set.add(old_value); // 记录原来的引用对象 记录B-D引用,当并发标记扫描结束,重新标记阶段我们可以从remark_set 取出B-D 应用直接将D对象标记为黑色 (G1垃圾收集器采用次算法)
}
1
2
3
4

写屏障实现增量更新

CMS

当对象A的成员变量的引用发生变化时,比如新增引用(a.d = d),我们可以利用写屏障,将A新的成员变量引用对象D记录下来:

void post_write_barrier(oop* field, oop new_value) {  
    remark_set.add(new_value);  // 记录新引用的对象 记录A->D 的引用
}
1
2
3

# CMS

基于标记清除算法,一种以获取最短回收停顿时间为目标的收集器,配合ParNew一起使用;

运行过程:

  1. 初始标记:stw,标记GC Roots对象;
  2. 并发标记:GC线程和用户线程一起工作,通过可达性分析,标记的是GC ROOTS引用链上的对象。这个阶段由于用户线程继续运行,可能导致已经标记过的对象状态发生改变;
  3. 重新标记:stw,主要标记并发标记阶段用户线程运行导致对象引用产生变动的那一部分对象(主要是处理漏标问题),采用三色标记法中的增量更新算法标记;
  4. 并发清理:GC线程与用户线程并发;

缺点:

  1. 对CPU 资源敏感和用户线程抢占CPU;
  2. 浮动垃圾;(标记清除算法)

# G1

满足GC停顿时间要求同时,具备高吞吐量的垃圾收集器;

将堆区划分为多个大小相等的 Region,每个 Region功能可能会动态变化(年轻代或老年代);

Young GC:

执行时机:计算Eden区回收时间,如果<<设定的参数( -XX:MaxGCPauseMills ),优先增加年轻代的Region,否则触发Young GC;

Mixed GC:

执行时机:老年代的堆区占有率打到设定参数(-XX:InitiatingHeapOccupancyPercent),采用复制算法,拷贝过程如果没有足够的空Region,触发Full GC;

运行过程:分为标记阶段,清理阶段,复制阶段;

  • 标记阶段:

    初始标记:同CMS(STW)

    并发标记:同CMS

    最终标记:同CMS(SATB)

  • 清理阶段:

    筛选回收:对回收价值排序,根据用户期望停顿时间制定回收计划;(STW)

  • 复制阶段:

    转移:分配新内存和复制对象的成员变量;(STW)

缺点:

  1. 记忆集记录跨region引用,使用写屏障维护记忆集操作;
  2. 复制阶段需要STW,停顿时间瓶颈;

# ZGC

参考:新一代垃圾回收器ZGC的探索与实践 - 美团技术团队 (meituan.com) (opens new window)

将将标记,转移,重定位阶段都做到了几乎并发的垃圾收集器;

运行过程:

img

关键技术:染色指针和读屏障技术,解决了转移过程中准确访问对象的问题,实现了并发转移。

并发转移问题:GC线程在转移对象的过程中,应用线程也在不停地访问对象,如果对象发生转移,但对象地址未及时更新,那么应用线程可能访问到旧地址,从而造成错误;

原理:使用染色指针判断对象是否被移动,如果发现被移动,“读屏障”会把读出来的指针更新到对象的新地址上,这样应用线程始终访问的都是对象的新地址;

类加载
调优与问题排查

← 类加载 调优与问题排查→

Theme by Vdoing | Copyright © 2021-2024
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式