垃圾收集算法与收集器
在 JVM 中,垃圾收集(Garbage Collection, GC)算法的核心目标是自动回收无用对象的内存,同时尽量减少对应用性能的影响。以下是 JVM 中主要垃圾收集算法的原理、流程及实际应用场景的详细介绍:
一、标记-清除算法(Mark-Sweep)
原理
- 标记阶段:从 GC Roots(如栈引用、静态变量)出发,遍历对象图,标记所有存活对象。
- 清除阶段:扫描堆内存,回收未被标记的对象所占用的内存(直接释放,不整理内存)。
工作流程
- 暂停所有应用线程(STW,Stop-The-World),启动标记。
- 标记完成后,清除未标记的垃圾对象。
- 恢复应用线程。
优点
- 实现简单,内存回收效率较高。
缺点
- 内存碎片化:清除后会产生不连续的内存空间,可能无法分配大对象,最终触发 Full GC。
- 两次 STW:标记和清除阶段均需暂停应用线程。
应用场景
- CMS 收集器的老年代回收(仅清除阶段,标记阶段并发执行)。
二、标记-整理算法(Mark-Compact)
原理
- 标记阶段:与标记-清除相同,标记所有存活对象。
- 整理阶段:将所有存活对象向内存一端移动,清理边界外的内存(消除碎片)。
工作流程
- STW 标记存活对象。
- 将存活对象移动到内存一端,保持连续。
- 清除边界外的内存。
- 恢复应用线程。
优点
- 无内存碎片:整理后内存连续,适合长期运行的应用。
- 空间利用率高:减少大对象分配失败的概率。
缺点
- 两次 STW 且时间长:移动对象需要额外时间,尤其是堆较大时。
- 内存移动开销:对象复制可能影响吞吐量。
应用场景
- Serial Old、Parallel Old 收集器的老年代回收。
- G1 的混合回收阶段(复制存活对象到新 Region)。
三、复制算法(Copying)
原理
- 将堆内存分为两块(From 和 To 空间),每次只使用其中一块。
- 存活对象复制:将 From 空间的存活对象复制到 To 空间,并保持紧凑排列。
- 清空 From 空间:复制完成后,直接清空 From 空间。
工作流程
- STW 标记 From 空间的存活对象。
- 将存活对象按顺序复制到 To 空间。
- 交换 From 和 To 空间的角色。
- 恢复应用线程。
优点
- 无碎片:对象在 To 空间连续排列。
- 高效回收:只需遍历存活对象,适合存活率低的场景。
缺点
- 内存浪费:需预留一半内存作为复制空间,实际可用内存仅 50%。
- 存活率高时效率低:若多数对象存活,复制成本高。
应用场景
- 年轻代回收(如 Serial、ParNew、Parallel Scavenge 收集器)。
- G1 的年轻代 Region 回收。
四、分代收集算法(Generational Collection)
核心思想
基于对象的生命周期特性,将堆划分为不同区域(年轻代、老年代),对不同代采用不同算法:
- 年轻代:对象存活率低,使用复制算法。
- 老年代:对象存活率高,使用标记-清除或标记-整理算法。
分代依据
- 弱分代假说(Weak Generational Hypothesis):绝大多数对象朝生夕死。
- 强分代假说(Strong Generational Hypothesis):多次存活的对象倾向于继续存活。
工作流程
- 年轻代 Minor GC:频繁触发,使用复制算法回收 Eden 和 Survivor 区。
- 老年代 Major GC/Full GC:触发频率低,使用标记-清除或标记-整理算法。
应用场景
- 所有现代 JVM 收集器的默认策略(如 CMS、G1、ZGC 等)。
五、增量收集算法(Incremental Collecting)
原理
- 将垃圾回收过程分为多个小步骤,与应用线程交替执行。
- 每次仅回收部分垃圾,减少单次 STW 时间。
优点
- 降低延迟:每次暂停时间短,适合实时性要求高的场景。
缺点
- 总体吞吐量下降:频繁切换线程导致额外开销。
- 实现复杂:需处理应用线程与回收线程的交互。
应用场景
- CMS 的并发标记阶段(并发执行,但需最终 STW 修正)。
- Shenandoah 收集器(并发复制对象)。
六、分区收集算法(Region-Based)
原理
- 将堆划分为多个独立区域(Region),优先回收垃圾比例高的区域(Garbage-First 原则)。
- 结合分代思想,动态分配区域角色(Eden、Survivor、Old)。
优点
- 可预测停顿:通过控制每次回收的区域数量,满足
MaxGCPauseMillis目标。 - 高效管理大堆:避免全堆扫描,减少 STW 时间。
应用场景
- G1 收集器:分 Region 管理,混合回收年轻代和老年代。
- ZGC/Shenandoah:基于类似思想,扩展为全堆并发回收。
七、算法对比与选型
| 算法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 标记-清除 | 实现简单,无移动开销 | 内存碎片,两次 STW | 老年代(CMS) |
| 标记-整理 | 无碎片,空间利用率高 | STW 时间长,移动开销大 | 老年代(Serial Old) |
| 复制 | 无碎片,回收效率高 | 内存浪费,存活率高时低效 | 年轻代(所有分代收集器) |
| 分代收集 | 结合对象生命周期优化 | 需维护分代结构 | 所有现代收集器 |
| 增量收集 | 低延迟 | 吞吐量下降 | 实时系统(Shenandoah) |
| 分区收集 | 可预测停顿,大堆友好 | 元数据管理复杂 | G1、ZGC、Shenandoah |
总结
JVM 的垃圾收集算法是内存管理的核心,不同算法在吞吐量、延迟和内存开销之间权衡。实际应用中,分代收集与分区算法(如 G1)成为主流,而 ZGC/Shenandoah 进一步通过并发和压缩技术突破停顿时间限制。选择算法需结合应用场景(如堆大小、延迟要求)及 JVM 版本特性。
以下以CMS和G1垃圾收集器的原理与工作流程举例:
CMS(Concurrent Mark-Sweep)收集器
设计目标
- 低延迟:通过并发标记和清除减少停顿时间(STW),适用于对响应时间敏感的应用(如Web服务)。
- 老年代专用:仅管理老年代,需与年轻代收集器(如ParNew)配合使用。
核心原理
- 标记-清除算法
- 通过标记存活对象,直接清除未标记的垃圾对象,不进行内存整理,可能导致内存碎片。
- 并发执行
- 大部分垃圾回收阶段与应用线程并发执行,减少STW时间。
工作流程(4个阶段)
-
初始标记(Initial Mark)
- STW:短暂暂停,标记从GC Roots直接可达的对象(如栈引用、静态变量)。
- 依赖年轻代回收:通常与年轻代的Minor GC同步触发,避免全堆扫描。
-
并发标记(Concurrent Mark)
- 并发执行:遍历老年代对象图,标记所有存活对象(与应用线程并行)。
- 可能漏标:并发阶段应用线程可能修改对象引用,需后续修正。
-
重新标记(Remark)
- STW:修正并发标记期间因应用线程运行导致的标记变化(使用增量更新或原始快照算法)。
- 时间较长:需处理所有变更,是CMS中最耗时的STW阶段。
-
并发清除(Concurrent Sweep)
- 并发执行:清除未标记的垃圾对象,回收内存空间。
- 不整理内存:清除后产生内存碎片,需定期Full GC(使用Serial Old)进行压缩。
关键问题
- 内存碎片:长期运行后可能触发Full GC,导致长时间停顿。
- 浮动垃圾:并发阶段新产生的垃圾需等到下次回收。
- CPU竞争:并发阶段占用CPU资源,可能影响应用吞吐量。
G1(Garbage-First)收集器
设计目标
- 平衡吞吐量与延迟:通过分Region和可预测停顿,适应大堆内存(4GB+)场景。
- 全堆管理:统一管理年轻代和老年代,动态分配Region角色。
核心原理
-
分Region模型
- 堆被划分为多个大小固定(1MB~32MB)的Region,每个Region可以是Eden、Survivor或Old类型。(堆/2048)
- Humongous区域:存储大于Region 50%的大对象。
-
标记-整理算法
- 通过复制存活对象到新Region,避免内存碎片。
-
停顿预测模型
- 根据用户设定的
MaxGCPauseMillis,优先回收垃圾比例高的Region(Garbage-First原则)。
- 根据用户设定的
工作流程(3种操作模式)
G1的回收过程分为三种操作:年轻代回收(Young GC)、并发标记周期(Concurrent Marking Cycle) 和 混合回收(Mixed GC)。
1. 年轻代回收(Young GC)
- STW:暂停所有应用线程,复制Eden和Survivor区的存活对象到新Survivor或Old Region。
- 触发条件:Eden区占满时触发。
2. 并发标记周期(Concurrent Marking Cycle)
-
初始标记(Initial Mark)
- STW:标记GC Roots直接可达的对象(与Young GC同步触发,复用Young GC的暂停)。
-
根区域扫描(Root Region Scan)
- 扫描Survivor区(作为GC Roots的一部分),确保并发标记的正确性。
-
并发标记(Concurrent Mark)
- 并发执行:遍历堆中所有存活对象,标记存活状态。
-
最终标记(Final Mark)
- STW:处理SATB(Snapshot-At-The-Beginning)队列中的引用变更,确保标记准确性。
-
清除阶段(Cleanup)
- 部分STW:统计各Region的垃圾比例,识别高价值回收区域。
- 不回收内存:仅选择待回收的Region,为混合回收做准备。
3. 混合回收(Mixed GC)
- STW:同时回收年轻代和部分老年代Region(根据垃圾比例选择)。
- 多次执行:可能分多次回收,直到满足
MaxGCPauseMillis目标。
关键优势
- 内存整理:通过复制算法避免碎片。
- 可预测停顿:动态调整回收的Region数量以满足停顿目标。
- 大堆优化:分Region设计适合管理超大堆内存。
CMS与G1对比
| 特性 | CMS | G1 |
|---|---|---|
| 堆结构 | 传统分代(年轻代+老年代) | 分Region,逻辑分代 |
| 算法 | 标记-清除(碎片需Full GC整理) | 标记-整理(复制算法,无碎片) |
| 停顿控制 | 不可预测 | 通过MaxGCPauseMillis预测 |
| 适用场景 | 中小堆,低延迟优先 | 大堆(4GB+),平衡吞吐量和延迟 |
| 内存开销 | 低 | 较高(Region元数据) |
| Full GC触发 | 内存不足/碎片时触发Serial Old | 尽量避免,依赖并发周期 |
| JDK支持 | 已废弃(JDK 14移除) | JDK 9+默认 |
总结
- CMS:通过并发标记-清除减少停顿,适合中小堆且延迟敏感的场景,但面临碎片和Full GC风险。
- G1:分Region模型和可预测停顿设计,适合大堆内存,平衡吞吐量与延迟,是JDK 9+的默认选择。
相关文章:
垃圾收集算法与收集器
在 JVM 中,垃圾收集(Garbage Collection, GC)算法的核心目标是自动回收无用对象的内存,同时尽量减少对应用性能的影响。以下是 JVM 中主要垃圾收集算法的原理、流程及实际应用场景的详细介绍: 一、标记-清除算法&#…...
【大模型】WPS 接入 DeepSeek-R1详解,打造全能AI办公助手
目录 一、前言 二、WPS接入AI工具优势 三、WPS接入AI工具两种方式 3.1 手动配置的方式 3.2 Office AI助手 四、WPS手动配置方式接入AI大模型 4.1 安装VBA插件 4.1.1 下载VBA插件并安装 4.2 配置WPS 4.3 WPS集成VB 4.4 AI助手效果测试 4.5 配置模板文…...
STM32步进电机驱动全解析(上) | 零基础入门STM32第五十七步
主题内容教学目的/扩展视频步进电机电路原理,跳线设置,驱动程序,调用控制。熟悉驱动程序,能调用控制。 师从洋桃电子,杜洋老师 📑文章目录 一、步进电机核心原理图解二、核心特性与优势三、关键驱动方式对比…...
Spring Boot 多数据源解决方案:dynamic-datasource-spring-boot-starter 的奥秘(上)
在 Spring Boot 生态中,dynamic-datasource-spring-boot-starter 是一个非常实用的组件,它为我们在多数据源场景下提供了便捷的解决方案。在上一篇文章《一分钟上手:如何创建你的第一个 Spring Boot Starter》中,我们学习了如何创…...
[NewStarCTF 2023 公开赛道]ez_sql1 【sqlmap使用/大小写绕过】
题目: 发现id处可以sql注入: 虽然输入id1;show databases;#没什么回显,但是知道这里是字符型注入了 这次利用sqlmap注入 --dbs:列出所有数据库名字 python .\sqlmap.py -u http://a40b2f0a-823f-4c99-b43c-08b94ed0abb2.node5.…...
【商城实战(18)】后台管理系统基础搭建:从0到1构建电商中枢
【商城实战】专栏重磅来袭!这是一份专为开发者与电商从业者打造的超详细指南。从项目基础搭建,运用 uniapp、Element Plus、SpringBoot 搭建商城框架,到用户、商品、订单等核心模块开发,再到性能优化、安全加固、多端适配…...
新能源汽车充电综合解决方案:安科瑞电气助力绿色出行
安科瑞 华楠 18706163979 随着新能源汽车的迅猛发展,充电基础设施的建设成为了推动行业进步的关键。然而,充电技术滞后、运营效率低下、车桩比失衡等问题,依然困扰着广大车主和运营商。今天,我们要为大家介绍一款新能源汽车充电…...
蓝桥杯java-B组真题—动态规划
目录 一.什么是动态规划? 二.题目 第一种情况:集合本身之和为奇数 第二种情况:集合本身之和为偶数 下面是代码实现: 一.什么是动态规划? 这里就简单的解释一下,动态规划就是记录之前的计算结果,避免重复的计算之前已经计算过的结果,用…...
【网络编程】事件选择模型
十、基于I/O模型的网络开发 10.9 事件选择模型 10.0.1 基本概念 事件选择(WSAEventSelect) 模型是另一个有用的异步 I/O 模型。和 WSAAsyncSelect 模 型类似的是,它也允许应用程序在一个或多个套接字上接收以事件为基础的网络事件通知,最 主要的差别在…...
网易邮箱如何用大数据任务调度实现海量邮件数据处理?Apache DolphinScheduler用户交流会上来揭秘!
你是否对大数据领域的前沿应用充满好奇?网易邮箱作为互联网大厂网易的重要业务线,在大数据应用方面有着诸多值得借鉴的实践经验。你是否渴望深入了解网易邮箱如何借助 Apache DolphinScheduler 实现海量邮件数据处理、用户行为分析、实时监控等核心业务场…...
前端知识点---路由模式-实例模式和单例模式(ts)
在 ArkTS(Ark UI 框架)中,路由实例模式(Standard Instance Mode)主要用于管理页面跳转。当创建一个新页面时,可以选择标准实例模式(Standard Mode)或单实例模式(Single M…...
固定表头、首列 —— uniapp、vue 项目
项目实地:也可以在 【微信小程序】搜索体验:xny.handbook 另一个体验项目:官网 一、效果展示 二、代码展示 (1)html 部分 <view class"table"><view class"tr"><view class&quo…...
langchain系列(九)- LangGraph 子图详解
目录 一、导读 二、原理说明 1、简介 2、子图图示 3、使用说明 三、基础代码实现 1、实现功能 2、Graph 图示 3、代码实现 4、输出 5、分析 四、人机交互 1、实现中断 2、历史状态(父图) 3、历史状态(子图) 4、历史…...
搜索引擎是如何理解你的查询并提供精准结果的?
目录 一、搜索引擎简单介绍 二、搜索引擎整体架构和工作过程 (一)整体分析 (二)爬虫系统 三个基本点 爬虫系统的工作流程 关键考虑因素和挑战 (三)索引系统 网页处理阶段 预处理阶段 反作弊分析…...
【前端】【组件】【vue2】封装一个vue2的ECharts组件,不用借助vue-echarts
在Vue2项目中使用ECharts 5.6的完整实现步骤如下: 安装依赖 npm install echarts5.6.2 --save # 指定安装5.x最新版本基础组件实现(新建components/ECharts.vue) <template><div ref"chartDom" class"echarts-co…...
18天 - 常见的 HTTP 状态码有哪些?HTTP 请求包含哪些内容,请求头和请求体有哪些类型?HTTP 中 GET 和 POST 的区别是什么?
常见的 HTTP 状态码有哪些? HTTP 状态码用于指示服务器对客户端请求的响应结果,常见的 HTTP 状态码可以分为以下几类: 1. 信息类(1xx) 100 Continue:客户端应继续发送请求。101 Switching Protocols&…...
IDEA软件安装环境配置中文插件
一、Java环境配置 1. JDK安装8 访问Oracle官网下载JDK8(推荐JDK8,11)Java Downloads | Oracle 双击安装程序,保持默认设置连续点击"下一步"完成安装 验证JDK安装,winR键 然后输入cmd,输入java…...
循环神经网络(RNN):时序建模的核心引擎与演进之路
在人工智能处理序列数据的战场上,循环神经网络(RNN)如同一个能够理解时间的智者。从 2015 年谷歌神经机器翻译系统颠覆传统方法,到 2023 年 ChatGPT 实现对话连续性,这些突破都植根于 RNN 对时序建模的深刻理解。本文将…...
HTML 表单 (form) 的作用解释
表单在网页中主要负责的是数据采集功能,一个表单基本由三部分组成: 表单标签:这里面包含了处理表单数据所用 CGI (Common Gateway Interface,通用网关接口)程序的 URL (Uniform Resource Locati…...
Windows控制台函数:控制台读取输入函数ReadConsoleA()
目录 什么是 ReadConsoleA? 它长什么样? 怎么用它? 它跟 std::cin 有什么不一样? 注意事项 什么是 ReadConsoleA? ReadConsoleA 是一个 Windows API 函数,用来从控制台读取用户输入。想象一下&#…...
网络tcp协议设置,网络tcp协议设置不了
网络TCP协议的设置通常涉及到多个方面,包括IP地址、子网掩码、默认网关、DNS服务器等参数的配置,以及TCP/IP协议栈本身的配置。如果遇到网络TCP协议设置不了的问题,可能是由多种原因导致的。以下是一些可能的原因及解决方法: 一、…...
电脑总显示串口正在被占用处理方法
1.现象 在嵌入式开发过程中,有很多情况下要使用串口调试,其中485/422/232转usb串口是非常常见的做法。 根据协议,接口芯片不同,需要安装对应的驱动程序,比如ch340,cp2102,CDM212364等驱动。可…...
R语言和RStudio安装
整体还是比较简单的,主要是记录个流程。 官方镜像站列表R语言官网 1 安装R(2025/3/6) R语言官网:The R Project for Statistical Computing 打开之后就Hello world一下吧 配置环境变量 2 安装RStudio 下载地址:htt…...
RHEL/CentOS 7.9使用firewalld限制出方向策略
背景 通常使用firewalld时候多为限制入方向访问,本次因有系统需要在生产环境部署测试环境,需求人希望在该测试环境中限制访问的对象,避免对生产造成影响 基础团队小伙伴参照rich-files,通过CLI,GUI反复进行进行配置验…...
设计模式之建造者模式:原理、实现与应用
引言 建造者模式(Builder Pattern)是一种创建型设计模式,它通过将复杂对象的构建过程分解为多个简单的步骤,使得对象的创建更加灵活和可维护。建造者模式特别适用于构建具有多个组成部分的复杂对象。本文将深入探讨建造者模式的原…...
1688店铺所有商品数据接口详解
一、接口概述淘宝开放平台提供 1688.items.onsale.get/taobao.item_search_shop 接口,可批量获取店铺在售商品列表,包含商品 ID、标题、价格、销量、图片等核心信息。该接口适用于商品库管理、竞品监控、数据分析等场景 二、接口调用流程 前期准…...
【C#学习笔记02】基本元素与数据类型
引言 深入了解C语言的基本元素、计算机存储器结构、常量与变量的概念以及数据类型。这些内容是C语言编程的基础,掌握它们对于编写高效、可靠的嵌入式程序至关重要。 1.C语言的基本元素 编程语言的发展离不开自然语言,所以编程语言的语法和词汇也是由…...
【语料数据爬虫】Python爬虫|批量采集工作报告数据(1)
前言 本文是该专栏的第4篇,后面会持续分享Python爬虫采集各种语料数据的的干货知识,值得关注。 在本文中,笔者将主要来介绍基于Python,来实现批量采集“工作报告”数据。同时,本文也是采集“工作报告”数据系列的第1篇。 采集相关数据的具体细节部分以及详细思路逻辑,笔…...
<建模软件安装教程1>Blender4.2系列
Blender4.2安装教程 0注意:Windows环境下安装 第一步,百度网盘提取安装包。百度网盘链接:通过网盘分享的文件:blender.zip 链接: https://pan.baidu.com/s/1OG0jMMtN0qWDSQ6z_rE-9w 提取码: 0309 --来自百度网盘超级会员v3的分…...
Docker极简部署开源播放器Splayer结合内网穿透远程流畅在线听歌
前言 嘿,各位音乐发烧友们!如果你厌倦了广告的打扰,渴望在忙碌的生活中找到一片宁静的音乐天地,那么今天这篇教程绝对适合你——如何在Ubuntu上用Docker快速搭建一款高颜值、无广告的某抑云音乐播放器Splayer。 Splayer不仅界面…...
