前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java垃圾收集器总结

Java垃圾收集器总结

原创
作者头像
eeaters
发布2022-03-06 22:03:20
4120
发布2022-03-06 22:03:20
举报
文章被收录于专栏:阿杰阿杰

总结一下在深入理解Java虚拟机中关于垃圾收集器的学习

  • 总结
    • 三指标
    • GC目前的发展趋势
    • ZGC与G1还有Shenandoah相比的核心优势
  • 如何判断对象可回收
    • 引用计数算法
    • 可达性分析算法
  • 引用
    • 强引用
    • 软引用
    • 弱引用
    • 虚引用
  • 对象死亡流程
  • 方法区的回收
  • 垃圾回收算法(追踪式或者可达性分析算法)
    • 分代理论
    • GC命名
    • 回收算法
  • HotSpot算法细节
    • OopMap
    • 安全点
    • 安全区域
    • 记忆集和卡表
    • 写屏障
    • 并发的可达性分析
  • 垃圾收集器
    • Serial收集器
    • ParNew收集器
    • Parallel Scavenge
    • Serial Old
    • Parallel Old
    • CMS
    • G1
    • Shenandoah
    • ZGC

总结

三指标

内存、延迟、吞吐量;

由于服务端占据主要使用面,因此低延迟是现在相对最看重的一个指标

GC目前的发展趋势

串行 → 并行 → 与用户线程并发 → STW可控,低延迟

ZGC与G1还有Shenandoah相比的核心优势

ZGC、G1和Shenandoah 都是以低延迟为主的收集器,总结了一下三者的区别

  1. Shenandoah是G1的plus版本, 使用图表以减低内存使用,通过进一步拆分回收阶段,进一步减少STW的阶段,原本是JDK12开始支持,但是肯定竞争不过ZGC,准备支持JDK1.8 ; 对于不想升级JDK版本但是想使用更好的GC,这是一个很好的选择
  2. ZGC出身正宗,因此通过直接在对象头上做文章,直接将Shenandoah做的一些工作给简化了;我感觉这是降维打击了

如何判断对象可回收

引用计数算法

概述

对象添加一个引用计数器,当有地方使用计数器就+1; 引用时效计数器-1,当计数器为零说明对象不可能被使用

  • 优点: 拥有原理简单,判定效率高
  • 缺点: 例外情况多,需要配送大量额外处理,比如循环引用问题

使用领域如: 微软的COM技术 ,ActionScript 3 的FlashPlayer, Python,游戏脚本领域等

可达性分析算法

概述

通过一系列 GC Roots 的跟对象作为起始节点集, 从这些节点为起点,根据引用关系往下搜索, 搜索过程走过的路径称为引用链(Reference Chain), 如果对象没有在任何引用链上,则说明对象不可达

JAVA中GC Roots对象包括:

  1. 虚拟机栈中(本地变量表)引用的对象,如:局部变量、临时变量等
  2. 方法区中类静态属性引用的对象
  3. 方法区中常量引用对象
  4. 本地方法栈中JNI引用的对象
  5. 虚拟机内部引用,如类加载器,基本类型对应的Class对象
  6. 所有被同步锁(synchronize)持有的对象
  7. JMXBean、JVMTI中注册的回调,本地代码缓存等

引用

判定对象是否存活和引用离不开关系,但是对象如果只有 被引用未被引用 的话,对于一些希望内存足够时先不回收就有些力不从心了,

JDK1.2后,引用的概念进行扩充,分为强引用,软引用,弱引用,虚引用

强引用

Strongly Reference

程序代码中普遍存在的引用赋值, 无论任何情况,GC都不会回收调该对象

软引用

Soft Reference

通过SoftReference类实现软引用, 描述还有用但非必须的对象; 系统在发生内存溢出异常前会尝试对这些对象进行回收,如果回收后内存还不足才会跑出内存溢出异常

弱引用

Weak Reference

通过WeakReference类实现弱引用,弱引用关联的对象只能生存到下一次垃圾收集发生时;

虚引用

Phantom Reference

通过PhantomReference类实现虚引用,又称:幽灵引用或者幻影引用; 无法通过虚引用获取对象实例, 虚引用的唯一目的是为了对象回收时收到一个系统通知

对象死亡流程

对象不可达≠对象死亡

死亡路线:

对象不可达 → 第一次标记 → 筛选需要执行finalize方法的对象 → 放到F-Queue对象中 → 执行finalize方法 → 第二次标记 → 回收,对象死亡

这里的finalize是老版本的一个妥协,finalize本身只会被调用一次;

如果没有覆盖finalize方法或者对象第二次发现没有引用链,会被认为不需要执行,然后进行回收

JDK 9 中标记为不推荐使用; JDK18应该会移除; 如果有资源需要释放应该try-finally进行处理

方法区的回收

Java虚拟机规范中提过方法区(JDK 1.8之前的永久代,JDK1.8之后的元空间)可以不实现垃圾回收;

譬如JDK 11时期的ZGC收集器;

不同多数垃圾回收期是会实现方法区垃圾回收的;方法区的垃圾回收主要有两部分内容:

  1. 废弃的常量
  2. 不在使用的类型(类的卸载)
  • 对于大量使用反射、动态代理、CGLib等字节码的框架动态生成JSP和OSGi这种场景通常需要虚拟机具备类型卸载能力以确保不会对方法区造成太大的内存压力
  • 判定条件
代码语言:txt
复制
- 类的所有实例都被回收了,堆中不存在任何该类和派生类的实例
- 加载这个类的类加载器已经被回收,一些场景会有临时的类加载器
- 这个Class对象没有任何地方被引用,无法通过反射访问该类的方法

垃圾回收算法(追踪式或者可达性分析算法)

分代理论

大多数垃圾回收机都是基于分代收集理论进行设计,分带理论基于连个分带假说建立:

  1. 弱分代假说: 大多数对象朝生夕灭
  2. 强分代假说: 熬过垃圾收集越多次的对象越难消亡
  3. 跨代引用假说: 跨带引用相对同代引用相比,只占极少数
  • 隐含结论: 互相引用关系的两个对象应该倾向于同时生存或者同时消亡,比如全局的缓存

基于分代理论的收集器将堆划分为不同的区域,然后基于不同的区域进行垃圾收集,实现垃圾回收的效率 ;

分代并不完美,因此最新出现的垃圾收集器都是面向全区域或者支持区域部分带的垃圾收集模式

GC命名

  1. 部分收集 - Partial GC ,不完全收集整个Java堆的都属于这个范畴
  • 新生代收集 - Minor GC / Young GC
  • 老年代收集 - Major GC / Old GC , *注: * 仅CMS收集器有单独收集老年代的行为,比如G1在Young GC回收不理想也是升级为混合GC的
  • 混合收集 - Mixed GC : 整个新生代+部分老年代,目前仅G1有混合行为整堆收集 - Full GC : 收集整个Java堆和方法区的垃圾收集;

回收算法

  1. 标记清除 : 标记要回收或者标记不回收的对象引用; 关注延迟的算法 大多数回收算法都基于这个算法进行改进的
  • CMS的垃圾回收就是基于这个算法
  • 缺点:
代码语言:txt
复制
- 执行效率不稳定, 可能会有大量标记和清理的动作,效率随对象增多而降低
- 内存空间碎片化问题, 碎片太多会导致因大对象没有足够连续空间而提前GC
- 碎片化必须依赖更为复杂的内存分配器和内存访问器来解决标记复制: 内存分两块,将存活的对象复制到另一块内存中
  • 适合新生代(IBM对新生代的特点进行了分析,新生代98%的对象第一轮GC就会被回收)
代码语言:txt
复制
- G1的新生代分为Eden和Survivor(又分from和to),默认比例: 8:1:1缺点:
代码语言:txt
复制
- 需要两份内存空间,空间浪费严重
- 如果存活对象太多,复制的开销会很大优点:
代码语言:txt
复制
- 实现简单,内存连续,对象分配高效标记整理: 存活对象往内存一端移动, 关注吞吐量
  • 适合有大量存活的对象,比如老年代
代码语言:txt
复制
- G1的老年代就是基于这种算法的缺点:
代码语言:txt
复制
- 整理时对象可能需要移动,移动存活对象并更新引用是极为负重的操作,此时就必须要STW(Stop The World);

扩充知识:

  1. 标记清理和标记整理算法在标记过程算法一样,但是后续步骤中,标记整理时会让所有存活的对象望内存空间的一端移动,然后清理掉边界以外的内存, 本质差异是清理时非移动式的回收算法,整理时移动式的回收算法.
  2. 标记整理如果每次都有大量对象,那么移动负担极重(移动存活对象并更新引用),必须暂停用户应用进程,即:STW
  3. 标记清理算法的碎片化问题需要依赖更复杂的内存分配器和内存访问器来解决,但是因为内存方式是用户最频繁的操作,这样会增加额外的负担,影响到应用程序的吞吐量
  4. 采用标记复制算法的G1默认比例8:1:1; 也就是说每次8+1的对象往1的内存空间中移动,那么如果超过10%的对象存活,那么这时候是通过依赖其他内存区域(基本就是老年代)进行分配担保;

HotSpot算法细节

OopMap

主流Java虚拟机使用准确式垃圾收集,用户线程停顿下来的时候并不需要一个不漏的检查所有执行上下文和全局的引用位置,应该有办法直接得到哪些地方存放对象引用的; HotSpot中是通过OopMap来存储

类加载动作完成时,HotSpot会把对象内什么偏移量是设么类型的数据计算出来,即使编译过程中也会在特定位置记录栈和寄存器里哪些位置是引用,收集器在扫码时就可以得知这些信息,并不需要GC Roots开始查找

安全点

有了OopMap,可以快速准确完成GC Roots枚举,但是引用关系可能发生变化;如果OopMap记录所有变换,那么空间成本会很高昂;

HotSpot是选择在特定位置生成安全点,GC时让所有线程都跑到最近的安全点然后停顿下来;

停顿方案有两种:抢先式中断、主动式中断(主动式是主流)

安全点的选择准则: 是否具有让程序长时间执行的特征 ,长时间的最基本特征就是指令序列的复用

比如: 方法调用、循环跳转、异常跳转等都属于指令序列复用

安全区域

安全点解决了如何丁顿用户线程让虚拟机可以进入垃圾回收状态,但是无法处理线程阻塞或睡眠状态;

安全区域指的是能够确保一段代码片段之中,引用关系不会发生变化,因此这个区域中任意位置进行来讲回收都是安全的,或者可以把安全区域看做安全点的扩展

用户线程进入安全区域时,会标识自己进入安全区域,当线程离开安全区域会检查虚拟机是否完成了根节点枚举,如果完成了继续执行,否则就必须一致等待,直到收到离开安全区域的信号为止

记忆集和卡表

记忆集是一种用于记录非收集区域指向了收集区域的指针集合的抽象数据结构

记忆集的实现方案比如:

  1. 字长精度,记录精确到处理器的寻址位数
  2. 对象精度,记录精确到一个对象,对象里面有字段包含跨带带指针
  3. 卡精度,记录精确到一块内存区域,该区域内有对象含有跨带指针

最常用的是卡精度,通过卡表进行实现的,所以卡表是记忆集的一种实现方式而已;

每一块区域是一个卡页, HotSpot中一个卡页的大小是2^9=512 字节,只要卡页内存在跨带指针,那么卡页变脏,垃圾回收时只针对变脏的卡页进行跨带扫描

写屏障

卡页什么时候进行更新呢?HotSpot是通过写屏障进行实现(和volatile中的写屏障不是一个概念);

引用类型字段赋值 阶段加入aop切面,引用对象复制产生一个环绕通知,虽然有一定开销,但是YoungGC的负担大大减小了,整体性能提升了

卡表并发时存在一个伪共享的问题;因此有一个 -XX:UseCondCardMark 的启动参数决定 不采用无条件的写屏障而是先检查卡表的标记再决定是否变为脏页

并发的可达性分析

标记阶段是所有追踪式垃圾收集器的共同特征,通常使用三色标记法来对来讲回收过程进行辅助推导:

  1. 白色: 分析前属于未被垃圾收集器访问过,分析后还是白色代表引用不可达
  2. 黑色: 对象已经被访问过, 并且这个对象的所有引用也扫描过,对象是安全存活的; 其他对象指向黑色对象不需要再次重新扫描;黑色对象必须经过灰色对象再指向白色对象
  3. 灰色: 对象已经被扫描过了; 但是存在至少一个引用还没有被扫描过;

在标记阶段用户线程和收集器在并发的执行;用户线程会修改引用关系,如果想存活对象标记为可收回,可能会引起致命的问题;这种情况的两个必要条件为:

  1. 赋值器插入了一条或者多条从黑色到白色对象的新引用
  • 破坏方案: 使用增量更新(简单理解:黑色变成了灰色), 实际应用: CMS赋值器删除了全部从灰色到该白色对象的直接或者间接引用
  • 破坏条件: 使用原始快照(简单理解:多版本控制机制,变换的先记录下来不处理;扫描结束后针对变化的再次进行扫描), 实际应用: G1,Shenandoah

垃圾收集器

Serial收集器

新生代收集器(基于标记-复制算法),通过单线程进行工作;

可以与CMS(基于标记-清理算法)和Serial Old(基于标记-整理算法)搭配使用

Serial收集器的最大优势就是简单而高效(与其他收集器的单线程比较) ;内存资源受限的环境中,它是所有收集器里面额外内存小号最小的; 是客户端模式下的默认新生代收集器

新生代内存占比小(如几十兆或者一两百兆),控制停顿时间一般可以控制在一百毫秒之内,只要不频繁发生收集,并且停顿用户可以接受,那么Serial是很好的选择,比如:

  1. 用户桌面应用
  2. 小型的微服务应用

ParNew收集器

新生代收集器;实质上是Serial的多线程并行版本;

虽然该收集器没有太多创新,但是由于从JDK1.5开始使用CMS进行老年代收集,能够和CMS配合的只有Serial和ParNew; 那么ParNew自然而然的成为了激活CMS时新生代的默认收集器

G1出现之后,官方希望推广G1,取消了CMS+Serial和ParNew+Serial Old组合; 所以CMS+ParNew只能互相搭配使用,ParNew被合并到了CMS中,成为了收个HotSpot中退出历史舞台的首款垃圾收集器

ParNew默认开启的收集线程数等于处理器的核心数量; 可以通过 -XX:ParallelGCThreads 来限制垃圾收集的线程数

Parallel Scavenge

新生代收集器;基于标记复制算法的多线程并行垃圾收集器;

CMS等收集器关注的是尽可能缩减垃圾收集时用户线程的停顿时间; 而 Parallel Scavenge关注的是达到一个可控制的吞吐量

吞吐量 = 运行用户代码时间 / (用户代码执行时间+垃圾收集时间)

停顿时间短的收集器适合于用户交互或者需要保证服务响应的质量2. 高吞吐量则可以高效的利用服务器资源,尽快的完成程序的运算任务,适合在后台运算而不需要太多交互的分析任务

Parallel Scavenge的特性:

  1. 吞吐量优先收集器
  2. 垃圾收集的自适应调节(开启-XX:UseAdaptiveSizePolicy后不需要人工设置eden,survivor的比例,晋升老年代的对象大小等参数西季节)

Serial Old

老年代收集器,基于标记-整理算法;

单线程收集器,主要意义

  1. 客户端模式下的HotSpot虚拟机使用;
  2. 服务端中:
  • JDK1.5之前与Parallel Scavenge搭配使用
  • CMS收集器发生失败时的后背元,在并发手机发生Concurrent Mode Failure时使用

Parallel Old

老年代收集,基于标记-整理算法; 支持多线程并发收集

JDK6 才开始提供; 也就是说在这个之前 Parallel Scavenge 只能与Serial Old进行搭配; 而Serial old又是单线程的,所以Parallel Scavenge的高吞吐量就有些名不符实了,甚至不如ParNew + CMS组合 ;

直到Parallel Old,Parallel Scavenge终于有了适合它的老年代收集器了

CMS

老年代垃圾收集; 基于标记-清理算法

目的: 获取最短回收停顿时间;

很大一部分的Java应用在互联网网站或者基于浏览器的B/S系统的服务端上; 应用通常都会特别关注服务的相应时间;希望停顿时间尽可能的短,提高用户体验;这些场景CMS就很适合使用;

CMS的整体过程分为四步:

  1. 初始标记 (STW)
  • 将GC Roots直接关联的对象进行标记,速度很快并发标记
  • 从初始标记的对象开始遍历整个引用链,可以与用户线程并发执行,耗时长重新标记(STW)
  • 修正并发标记时因用户线程导致的标记变动,相对初始标记耗时长,但是相对并发标记耗时很短并发清除
  • 清理标记可回收的对象,耗时长

CMS是一款并发低停顿收集器; 存在三个明显缺点

  • 对处理器资源非常敏感: 虽然可以和用户线程并发执行;但是势必需要占用一定系统资源; 默认启动回收线程数 = (CPU核数+3)/4 ;在4核以上的服务器上,占用⇐25%资源;
  • 无法处理浮动垃圾; 因为并发标记和并发清理阶段是和用户线程并发执行; 因此标记时相对滞后的,只能处理开始标记的时间点之前的对象; 无法处理标记过程后产生的垃圾对象; 这部分对象就是 浮动垃圾 ; 为了
  • 浮动垃圾可能导致提前FullGC的出现;导致STW的出现;JDK5默认情况下,当老年代内存使用68%就会触发GC ; 那么如何预测到老年代增长不是特别快可以适当调大这个值来减少频繁的GC提升性能; JDK6后,该值默认提升为92%; 如果CMS运行期间内存无法满足对象分配,那么虚拟机会冻结用户线程,临时启用Serial Old 来对老年代进行回收; 这时候STW停顿时间会很长空间碎片问题;这个是和算法相关; 因为标记-清理是原地进行清理的,造成了内存不连续,大对象分配空间可能导致提前触发Full GC,会产生STW

G1

Garbage First : 主要面向服务端应用的垃圾收集器,基于"停顿时间模型"收集器

目的: 关注停顿时间,可控制的停顿时间

G1开创了基于Region的堆内存分布模型;维护了一个优先级的回收队列. 通过对Region进行评估,在不超过期望停顿时间的约束下,选择对应的Region获取最高的收益;

抛开与用户线程并发执行的过程,G1的运作过程大概分 四个步骤

  1. 初始标记. 耗时短, 借用Minor GC时同步完成,没有额外停顿
  2. 并发标记. 耗时长, 可与用户线程并发执行, G1采用SATB(原始快照)的方式, 对象图扫描完成后,还需要重新处理SATB记录下的并发时引用变动的对象
  3. 最终标记. 对用户线程做一个短暂的停顿, 处理并发阶段遗留下来的少量SATB记录
  4. 筛选回收. 更新Region的统计数据,对各个Region的回收价值和成本进行排序, 根据用户期望的停顿时间制定回收计划. 将Region中存活的对象复制到空的Region中,再清理到旧的Region空间.涉及对象移动,必须暂停用户线程, 由多条收集器线程并行完成

G1与CMS

因为G1和CMS都是比较关注停顿时间的垃圾收集器,用两个进行比较;

  1. 回收算法
  • CMS: 标记清理
  • G1: 整体标记整理, 局部(从Region的角度上来看)标记复制;内存占用; 都使用卡表来处理跨带引用
  • CMS: 卡表结构简单,内存占用小
  • G1: 卡表实现复杂,可能需要占用堆容量的20%甚至更多的内存空间; 另外,G1还需要耗费大约Java堆容量的10%~20%的额外内存来维持收集器的工作执行负载
  • CMS: 使用写后屏障来更新维护卡表
  • G1: 使用写前屏障跟踪并发时指针变化,使用写后屏障维护卡表
  • 原理: CMS使用增量,G1使用快照; 快照可以有效的减少并发标记和重新标记阶段的消耗避免标记阶段停顿过长, 但是与用户并发执行会产生由于跟踪引用变化带来的额外负担; G1使用类似消息队列的实现方式异步处理写前写后操作使用场景
  • 目前小内存中CMS表现大概率仍然优于G1, 大内存应用上G1则能发挥其优势; 优劣的平衡点通常在6GB~8GB之间;

Shenandoah

JDK12中出现的一款强大但是由于不是Oracle或者Sun出品(RedHat出品)导致一直被排挤的垃圾收集器; 在OpenJDK中存在但是没有存在于OracleJDK ;

目标: 低停顿 ; 目前RedHat积极扩展其使用范围,将其能够在JDK11甚至JDK8上,让不方便升级JDK版本的应用也能够享受垃圾收集技术的前沿成果

G1是现在使用比较广泛的垃圾收集器; 但是G1的重点在与最耗时的标记阶段可以与用户线程并发执行; 目标就是在延迟可控的情况下尽可能的高吞吐,但是回收阶段G1只能做到多个线程并发回收,此时用户线程必须中断 ;

Shenandoah在G1的基础上进行改善,

  • 通过连接矩阵来记录跨Region的引用关系,和G1相比,大大降低内存占用,降低了伪共享及几率;
  • 通过使用转发指针使得并发回收阶段也能够与用户线程并发执行,

目前Shenandoah大概分为了九个阶段

  1. 初始标记: STW,但是停顿时间只与GCRoots数量有关
  2. 并发标记: 同G1,可与用户线程并发执行,时间长短取决于堆中存活对象的数量及结构复杂程度
  3. 最终标记: 同G1,处理剩余的SATB扫描,并在这个阶段统计回收价值最高的Region,构成回收集,会有一小段时间的停顿
  4. 并发清理: 用于清理整个区域一个存活对象都没有的Region
  5. 并发回收:
  • G1在回收阶段虽然可以设置期望停顿时间进行控制,但是依旧是STW的
  • 将回收集中存活的对象复制一份到其他未被使用的Region之中,与用户线程并发时通过转发指针来解决,时间长短取决于回收集的大小初始引用更新, 上一个阶段中会出现指向旧对象的引用, 那么修需要将引用进行更新, 引用更新拆分了三个步骤,初始引用更新这一步没有真正开始处理,这个阶段需要建立线程集合点, 也就是做好下一步的准备工作并发引用更新, 开始进行引用更新; 将旧的地址更新为新的地址; 这一步可以与用户线程并发执行;时间长短取决于涉及到的引用数量; 并且这一步不需要扫描,是已经确定好了需要处理的引用集合最终引用更新: 上一步做好了非GCRoots的引用更新,这一步处理GCRoots的引用更新并发清理: Shenandoah和G1一样,局部采用标记复制算法,这一步就是将旧的Region进行清理

ZGC

JDK11中新增加基于标记-整理的低延迟垃圾收集器(服务端最关注的就是低延迟), JDK11也是LTS版本,很多公司通过升级JDK到11来使用ZGC这款来收集器

之前的GC是基于当前JVM规范之上进行相应的处理措施,而ZGC是通过在使用引用对象的指针上新增了四个标志位(染色指针), 通过标记位可以快速的确认对象的三色标记状态,是否重分配,是否只能通过finalize访问这些信息; 对于GC这个过程,只要知道这些信息就够了,所以与之前的GC相比,就不需要先获取引用再获取引用对应的信息; 对应,因为压缩了对象的地址空间,所以ZGC管理的内存不可以超过4TB(这个我目前碰到大数据对内存使用比较大,也不过就128G…)

ZGC的三个优势:

  1. Region能够更快速的释放和重用,
  2. 大幅度减少垃圾收集中内存屏障的使用; 因此除了延迟,ZGC对吞吐量影响也相对较低
  3. 可扩展性强,ZGC扩展了对象头,后续就可以在这里做一些文章进一步提升性能

ZGC的运作过程

1. 并发标记; 更新染色指针

2. 并发预备重分配: 扫描整个堆得到本次收集过程中要清理的region(重分配集)

3. 并发重分配: 核心阶段, 将存活对象复制到新的region中; 如果访问旧对象会被映射到新的对象地址上(自愈能力)

4. 并发重映射: 虽然有自愈能力; 但是依旧会主动修正引用;同时也由于有自愈能力;这一步并不是一个迫切的任务

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 总结
    • 三指标
      • GC目前的发展趋势
        • ZGC与G1还有Shenandoah相比的核心优势
        • 如何判断对象可回收
          • 引用计数算法
            • 概述
          • 可达性分析算法
            • 概述
        • 引用
          • 强引用
            • 软引用
              • 弱引用
                • 虚引用
                • 对象死亡流程
                • 方法区的回收
                • 垃圾回收算法(追踪式或者可达性分析算法)
                  • 分代理论
                    • GC命名
                      • 回收算法
                      • HotSpot算法细节
                        • OopMap
                          • 安全点
                            • 安全区域
                              • 记忆集和卡表
                                • 写屏障
                                  • 并发的可达性分析
                                  • 垃圾收集器
                                    • Serial收集器
                                      • ParNew收集器
                                        • Parallel Scavenge
                                          • Serial Old
                                            • Parallel Old
                                              • CMS
                                                • G1
                                                  • Shenandoah
                                                    • ZGC
                                                    相关产品与服务
                                                    领券
                                                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档