Interview AiBoxInterview AiBox 实时 AI 助手,让你自信应答每一场面试
Java中有哪些垃圾回收算法?请分别介绍它们的特点
题型摘要
Java中主要有9种垃圾回收算法:1)标记-清除算法:基础算法,分标记和清除阶段,但会产生内存碎片;2)标记-复制算法:将内存分两块,只使用一块,用完后复制存活对象到另一块,无碎片但内存利用率低;3)标记-整理算法:标记后移动存活对象到一端,再清理边界外内存,无碎片但效率低;4)分代收集算法:将堆分为新生代和老年代,不同代使用不同算法;5)增量收集算法:将GC分解为多个小步骤,减少单次停顿时间;6)CMS:并发标记清除,低延迟但CPU敏感;7)G1:面向服务端,可预测停顿时间,空间整合;8)ZGC:极低延迟,支持大堆内存;9)Shenandoah:低延迟,实现并发压缩。不同算法适用于不同场景,选择需考虑应用特点、硬件资源和性能需求。
Java垃圾回收算法详解
Java的垃圾回收(Garbage Collection, GC)是自动内存管理的核心机制,通过不同的算法来识别和回收不再使用的对象,释放内存空间。以下是Java中主要的垃圾回收算法及其特点。
1. 标记-清除算法 (Mark-Sweep)
工作原理
标记-清除算法是最基础的垃圾回收算法,分为两个阶段:
- 标记阶段:从GC Roots根节点出发,遍历所有可达对象,并给它们打上标记。
- 清除阶段:遍历整个堆内存,回收所有未被标记的对象(即垃圾对象)。
特点
-
优点:
- 实现简单,基础算法
- 不需要移动对象,适合对象存活率高的情况
-
缺点:
- 内存碎片化:回收后的内存空间不连续,产生大量内存碎片
- 分配效率低:分配大对象时可能难以找到足够的连续空间
- 标记和清除效率都不高:需要遍历所有对象,包括存活和垃圾对象
2. 标记-复制算法 (Mark-Copy)
工作原理
标记-复制算法将可用内存按容量划分为大小相等的两块,每次只使用其中一块。当这一块内存用完时,就将还存活的对象复制到另一块内存上,然后一次性清理掉整个使用过的内存空间。
特点
-
优点:
- 无内存碎片:存活对象被紧凑地复制到另一块内存,内存空间连续
- 分配效率高:内存分配时不需要考虑碎片问题
- 回收简单高效:只需处理存活对象,不需要处理垃圾对象
-
缺点:
- 内存利用率低:只能使用一半的内存空间
- 复制成本高:当对象存活率高时,复制操作的成本会很高
- 不适合老年代:老年代对象存活率高,复制成本大
3. 标记-整理算法 (Mark-Compact)
工作原理
标记-整理算法结合了标记-清除和标记-复制的优点。标记阶段与标记-清除算法相同,但在清除阶段不是直接回收垃圾对象,而是将所有存活对象向内存空间的一端移动,然后直接清理掉端边界以外的内存。
特点
-
优点:
- 无内存碎片:存活对象被紧凑地排列在内存一端
- 内存利用率高:不需要像复制算法那样预留一半空间
- 适合老年代:对于存活率高的老年代对象,移动成本低于复制成本
-
缺点:
- 效率较低:不仅要标记存活对象,还要整理它们的内存位置
- 暂停时间较长:对象移动需要更新所有引用这些对象的地方
4. 分代收集算法 (Generational Collection)
工作原理
分代收集算法基于"分代假说":绝大多数对象都是朝生夕灭的,存活越久的对象越难以消亡。它将Java堆内存分为新生代(Young Generation)和老年代(Old Generation),不同代使用不同的收集算法。
- 新生代:使用标记-复制算法,因为新生代中对象存活率低
- 老年代:使用标记-清除或标记-整理算法,因为老年代中对象存活率高
特点
-
优点:
- 针对不同代使用最优算法:新生代使用复制算法,老年代使用标记-清除或标记-整理
- 回收效率高:大部分对象在新生代就被回收,减少了老年代的GC频率
- 减少停顿时间:新生代GC(Minor GC)频繁但快速,老年代GC(Major GC/Full GC)不频繁但耗时
-
缺点:
- 实现复杂:需要维护不同代的内存结构和对象晋升机制
- 内存碎片问题:老年代使用标记-清除算法时仍会产生内存碎片
5. 增量收集算法 (Incremental Collection)
工作原理
增量收集算法将垃圾回收过程分解为一系列小的步骤,在应用程序运行的间隙逐步执行,而不是一次性完成整个GC过程。通过这种方式,可以减少单次GC的停顿时间。
特点
-
优点:
- 减少停顿时间:将GC工作分散到多个时间段,避免长时间停顿
- 提高响应性:适合对响应时间要求高的应用
-
缺点:
- 总体吞吐量降低:由于GC与应用程序交替执行,上下文切换增加开销
- 实现复杂:需要处理GC与应用程序之间的同步问题
- 可能需要写屏障(Write Barrier):来维护对象引用的一致性
6. 并发标记清除算法 (Concurrent Mark-Sweep, CMS)
工作原理
CMS是一种以获取最短回收停顿时间为目标的收集器,主要针对老年代。它将垃圾回收过程分为多个阶段,其中大部分阶段可以与用户线程并发执行。
CMS收集器主要包括四个阶段:
- 初始标记(Initial Mark):暂停用户线程,仅标记GC Roots直接关联的对象
- 并发标记(Concurrent Mark):与用户线程并发执行,遍历对象图进行标记
- 重新标记(Remark):暂停用户线程,修正并发标记期间因用户程序运行而导致标记产生变动的那部分对象
- 并发清除(Concurrent Sweep):与用户线程并发执行,清除已标记为垃圾的对象
特点
-
优点:
- 低延迟:大部分GC工作与用户线程并发执行,停顿时间短
- 适合对响应时间要求高的应用:如Web服务、交互式应用
-
缺点:
- CPU敏感:并发阶段会占用CPU资源,可能影响应用程序性能
- 产生内存碎片:使用标记-清除算法,会产生内存碎片
- 并发模式失败:如果在并发清理期间,用户线程产生了大量新对象,而老年代没有足够空间容纳,就会触发"并发模式失败",此时会退化为使用Serial Old收集器进行Full GC
- 浮动垃圾:由于并发标记和清理期间用户线程仍在运行,会产生新的垃圾对象,这些对象只能在下次GC时回收
7. G1收集器 (Garbage-First)
工作原理
G1收集器是面向服务端的、可预测停顿时间的垃圾收集器。它将整个Java堆划分为多个大小相等的独立区域(Region),并跟踪每个Region中垃圾的价值(回收所获得的空间大小以及回收所需时间的经验值),在有限的时间内优先回收价值最大的Region(即"Garbage-First"名称的由来)。
G1收集器主要包括以下几个阶段:
- 初始标记(Initial Mark):暂停用户线程,仅标记GC Roots直接关联的对象
- 并发标记(Concurrent Mark):与用户线程并发执行,遍历对象图进行标记
- 最终标记(Final Mark):暂停用户线程,处理并发标记结束后遗留的少量SATB(快照开始)记录
- 筛选回收(Live Data Counting and Evacuation):暂停用户线程,根据各个Region的回收价值和成本进行排序,根据用户期望的GC停顿时间制定回收计划,回收选定的Region中的垃圾对象
特点
-
优点:
- 可预测的停顿时间:可以通过参数-XX:MaxGCPauseMillis指定期望的最大停顿时间
- 空间整合:从整体上看是基于标记-整理算法,从局部(两个Region之间)看是基于复制算法,不会产生内存碎片
- 并行与并发:能充分利用多CPU、多核环境,缩短Stop-The-World时间
- 分代收集:仍然保留了分代概念,但不需要物理上连续的新生代和老年代
-
缺点:
- 内存占用:G1的记忆集(Remembered Set)和其他执行开销会占用更多的内存
- 小内存场景下可能不如CMS:在小内存(如6GB以下)的情况下,G1可能不如CMS高效
- 计算回收成本:需要额外计算Region的回收价值和成本,增加了一定的CPU开销
8. ZGC收集器 (Z Garbage Collector)
工作原理
ZGC是JDK 11引入的低延迟垃圾收集器,目标是在尽可能不影响吞吐量的情况下,实现任意堆大小下的低延迟(通常低于10ms)。ZGC使用了着色指针(Colored Pointers)和读屏障(Load Barriers)技术,使得大部分GC工作可以与用户线程并发执行。
ZGC的主要阶段包括:
- 标记阶段(Mark):并发标记所有存活对象
- 重定位阶段(Relocate):选择需要重定位的Region,并创建重定位集(Relocation Set)
- 重映射阶段(Remap):并发更新所有指向重定位集中对象的引用
特点
-
优点:
- 极低延迟:停顿时间通常不超过10ms,与堆大小无关
- 支持大堆内存:可以轻松管理TB级别的堆内存
- 高吞吐量:与应用程序并发执行,对吞吐量影响小
- 无需内存压缩:通过重定位技术,避免了传统的内存压缩操作
-
缺点:
- CPU开销:使用读屏障会增加CPU开销
- 内存占用:ZGC的多映射技术会占用更多的物理内存
- 兼容性:需要较新的操作系统和硬件支持
9. Shenandoah收集器
工作原理
Shenandoah是Red Hat公司开发的低延迟垃圾收集器,在JDK 12中被正式引入。与ZGC类似,Shenandoah也追求低延迟,但采用了不同的技术实现。Shenandoah使用连接指针(Brooks Pointers)和读屏障/写屏障技术,实现了并发压缩。
Shenandoah的主要阶段包括:
- 初始标记(Initial Mark):暂停用户线程,标记GC Roots直接关联的对象
- 并发标记(Concurrent Mark):与用户线程并发执行,遍历对象图进行标记
- 最终标记(Final Mark):暂停用户线程,完成标记工作
- 并发清理(Concurrent Cleanup):与用户线程并发执行,回收立即垃圾对象
- 并发 evacuation(Concurrent Evacuation):与用户线程并发执行,将存活对象复制到新的Region
- 并发引用更新(Concurrent Update References):与用户线程并发执行,更新所有指向已移动对象的引用
特点
-
优点:
- 低延迟:停顿时间短,通常在10ms以内
- 并发压缩:实现了并发 evacuation和引用更新,减少了停顿时间
- 与堆大小无关:停顿时间不随堆大小的增加而增加
- 开放源代码:与ZGC不同,Shenandoah是完全开源的
-
缺点:
- CPU开销:使用读屏障和写屏障会增加CPU开销
- 内存占用:需要额外的内存来支持并发操作
- 相对较新:相比其他收集器,Shenandoah较新,可能存在一些未知问题
垃圾回收算法对比
| 算法 | 适用区域 | 优点 | 缺点 | 停顿时间 | 内存利用率 |
|---|---|---|---|---|---|
| 标记-清除 | 老年代 | 实现简单 | 内存碎片化 | 中 | 高 |
| 标记-复制 | 新生代 | 无内存碎片 | 内存利用率低 | 短 | 低(50%) |
| 标记-整理 | 老年代 | 无内存碎片 | 效率较低 | 长 | 高 |
| 分代收集 | 全堆 | 针对不同代使用最优算法 | 实现复杂 | 短(新生代)/长(老年代) | 高 |
| 增量收集 | 全堆 | 减少单次停顿时间 | 总体吞吐量低 | 短 | 高 |
| CMS | 老年代 | 低延迟 | CPU敏感、内存碎片 | 短 | 高 |
| G1 | 全堆 | 可预测停顿时间 | 内存占用高 | 可控 | 高 |
| ZGC | 全堆 | 极低延迟 | CPU开销大 | 极短(<10ms) | 中 |
| Shenandoah | 全堆 | 低延迟、并发压缩 | CPU开销大 | 短(<10ms) | 中 |
总结
Java的垃圾回收算法经历了从简单到复杂、从单线程到并发、从高停顿到低延迟的发展过程。不同的算法适用于不同的场景和需求:
- 标记-清除、标记-复制和标记-整理是最基础的算法,现代垃圾收集器大多基于这些算法的组合和改进
- 分代收集是现代JVM的基础,将堆分为新生代和老年代,针对不同代使用不同的收集策略
- CMS、G1、ZGC和Shenandoah是面向低延迟的现代收集器,通过并发执行大部分GC工作来减少停顿时间
选择合适的垃圾回收算法需要考虑应用的特点、硬件资源和性能需求。对于大多数应用,G1是一个很好的默认选择;对于特别关注低延迟的应用,可以考虑ZGC或Shenandoah;对于资源受限的环境,可能需要考虑更传统的收集器。
参考资料与延伸阅读
思维导图
Interview AiBoxInterview AiBox — 面试搭档
不只是准备,更是实时陪练
Interview AiBox 在面试过程中提供实时屏幕提示、AI 模拟面试和智能复盘,让你每一次回答都更有信心。
AI 助读
一键发送到常用 AI
Java中主要有9种垃圾回收算法:1)标记-清除算法:基础算法,分标记和清除阶段,但会产生内存碎片;2)标记-复制算法:将内存分两块,只使用一块,用完后复制存活对象到另一块,无碎片但内存利用率低;3)标记-整理算法:标记后移动存活对象到一端,再清理边界外内存,无碎片但效率低;4)分代收集算法:将堆分为新生代和老年代,不同代使用不同算法;5)增量收集算法:将GC分解为多个小步骤,减少单次停顿时间;6)CMS:并发标记清除,低延迟但CPU敏感;7)G1:面向服务端,可预测停顿时间,空间整合;8)ZGC:极低延迟,支持大堆内存;9)Shenandoah:低延迟,实现并发压缩。不同算法适用于不同场景,选择需考虑应用特点、硬件资源和性能需求。
智能总结
深度解读
考点定位
思路启发
相关题目
请做一个自我介绍
自我介绍是HR面试的开场问题,考察表达能力、逻辑思维、自我认知、岗位匹配度和沟通技巧。有效的自我介绍应包含基本信息、教育背景、专业技能、项目/实习经历、个人特质与岗位匹配、求职动机与未来规划。表达时应控制时间在2-3分钟,语言简洁,重点突出,真诚自然。针对客户端开发岗位,应强调相关技术栈、项目经验和注重细节的特质。避免内容过于简单或冗长,缺乏针对性,过度夸大或缺乏逻辑性。建议提前准备、反复练习、突出亮点、保持真实并积极互动。
你的期望薪资是多少?
回答"期望薪资"问题需先做市场调研和自我评估,面试时应表达对职位的兴趣,提供合理薪资范围而非具体数字,强调综合考量整体薪酬包和发展机会,保持灵活态度并适时反问公司预算。避免过低或过高报价,关注长远职业发展。
请做一个自我介绍,包括你的教育背景、技术栈和项目经验。
自我介绍应包含教育背景、技术栈和项目经验三部分。首先简述基本信息,然后详细介绍与岗位相关的教育经历,清晰列出掌握的技术及熟练程度,选择2-3个代表性项目按STAR法则描述。最后强调个人优势与职业规划,表达对公司的向往。整个介绍应控制在3-5分钟,保持真实、有针对性,自信表达,并准备好对介绍内容的深入回答。
请详细介绍你的项目背景、技术选型、实现难点以及你的具体贡献。
这个问题要求面试者介绍项目背景、技术选型、实现难点和个人贡献。回答时应简明扼要地介绍项目目标和规模,详细说明技术选型理由,分析遇到的技术难点及解决方案,并清晰阐述个人在项目中的角色和贡献。通过展示项目经验、技术决策能力、问题解决能力和团队协作能力,全面体现面试者的综合素质和专业水平。
你在大学期间哪门计算机课程学得最好?为什么?
在大学期间,我学得最好的课程是数据结构与算法。通过理论与实践结合的学习方法,我深入掌握了各种数据结构和算法的核心知识点,并将这些知识应用到多个实际项目中。这些知识对客户端开发尤为重要,可以帮助优化性能、提升用户体验、有效管理内存和优化界面渲染。我持续学习算法的热情和扎实的基础,将帮助我在客户端开发实习中做出贡献。