当前位置: 首页 > news >正文

JVM 垃圾收集算法详解!

在这里插入图片描述

目录

      • 一、 内存管理的挑战与垃圾收集的必要性
      • 二、 基础垃圾收集算法
        • 1. 标记-清除(Mark-Sweep)算法
        • 2. 复制(Copying)算法
        • 3. 标记-整理(Mark-Compact)算法
      • 三、 HotSpot JVM的基石:分代收集(Generational Collection)算法
        • 1. 新生代(Young Generation)
        • 2. 老年代(Old Generation)
        • 分代收集的工作流程(简化版):
      • 四、 总结与展望

摘要:在Java的GC机制中,垃圾收集算法是其核心基石。理解这些算法,是Javacode开发者掌握JVM内存管理、排查内存泄漏和JVM性能调优不可或缺的一步。本文将详细剖析JVM中最常用的几种基本垃圾收集算法的原理、优缺点及其在HotSpot JVM中的实际应用,揭示它们如何共同协作,构建起一套高效的自动化内存管理系统。


一、 内存管理的挑战与垃圾收集的必要性

在Java世界里,我们程序员无需手动为新创建的对象分配和释放内存,JVM的垃圾收集器(Garbage Collector, GC)会自动接管这项繁重而易错的工作。这种“自动挡”的内存管理机制,极大地提高了开发效率,减少了内存泄漏和野指针等问题。

然而,GC并非神秘魔法,它的背后是一系列精妙的算法在支撑。理解这些算法,是理解GC行为与进行JVM性能调优的起点。

在深入算法之前,我们首先要知道GC是如何判断一个对象是否是“垃圾”的。现代JVM普遍采用可达性分析(Reachability Analysis) 算法:从被称为“GC Roots”的根对象出发,遍历所有的引用链。如果一个对象无法通过任何引用链从GC Roots到达,那么它就被判定为“不可达”,即为可回收的垃圾。

二、 基础垃圾收集算法

接下来,我们逐一介绍三种最基础也是最重要的垃圾收集算法。

1. 标记-清除(Mark-Sweep)算法

这是最古老、最基础的垃圾收集算法,也是许多其他更复杂算法的基础。它分为两个主要阶段:

  • 标记(Mark)阶段:垃圾收集器从GC Roots开始遍历,标记所有可达(即存活)的对象。
  • 清除(Sweep)阶段:标记完成后,垃圾收集器会遍历整个堆内存,回收所有未被标记的对象,即所谓的“垃圾”,并释放它们所占用的内存空间。

工作原理图示:

在这里插入图片描述

优点:

  • 实现相对简单。

缺点:

  • 效率低下:标记和清除两个过程都需要遍历整个堆,耗时较长。
  • 内存碎片化(Fragmentation):清除过程不会移动存活对象,导致释放后的内存空间是不连续的。如果堆内存中产生大量不连续的碎片,当需要分配大对象时,即使总的空闲内存足够,也可能找不到一块连续的内存空间来分配,从而触发另一次GC(提前Full GC),影响性能。
2. 复制(Copying)算法

为了解决标记-清除算法的效率和内存碎片问题,复制算法应运而生。它将可用内存按容量划分为大小相等的两块,每次只使用其中一块。

工作原理:
当一块内存用完时,垃圾收集器就将这块内存中所有存活的对象,复制到另一块空闲的内存上,然后一次性清空已使用过的内存。

工作原理图示:

在这里插入图片描述

优点:

  • 运行高效:不需要遍历整个堆,只需遍历存活对象并进行复制,然后直接清空已使用区域,避免了标记和清除阶段的复杂性。
  • 没有内存碎片:存活对象被复制到新区域时是紧凑排列的,保证了内存的连续性。

缺点:

  • 内存利用率低:每次只有一半的内存空间被利用,空间浪费严重。

鉴于其“高效率”和“无碎片”的优点,复制算法在年轻代(Young Generation) 中得到了广泛的应用。通常,年轻代由一个Eden区和两个大小相等的Survivor区(S0和S1)组成。HotSpot JVM的默认分配比例为Eden:S0:S1 = 8:1:1,总共只浪费10%的内存(S0或S1中某一个始终为空闲)。

3. 标记-整理(Mark-Compact)算法

标记-整理算法是标记-清除算法的改进版,它在标记存活对象后,不是直接清除,而是将所有存活对象向一端移动,然后清理掉边界以外的内存。

工作原理:

  1. 标记(Mark)阶段:同标记-清除算法,标记所有存活对象。
  2. 整理(Compact)阶段:将所有被标记为存活的对象,都移动到内存空间的某一端。
  3. 清除(Clear)阶段:直接清理掉被移动对象边界以外的所有内存,从而得到一段完整而连续的空闲空间。

工作原理图示:

在这里插入图片描述

优点:

  • 没有内存碎片:解决了标记-清除算法的内存碎片问题。
  • 内存利用率高:没有像复制算法那样牺牲一半内存。

缺点:

  • 效率低下:除了标记阶段,整理阶段也需要移动所有存活对象。这个移动操作,尤其是在老年代对象多且大时,是一个耗时且会造成较长停顿(Stop-The-World, STW)的操作。

标记-整理算法在老年代(Old Generation) 中被广泛使用,因为老年代的对象存活率通常较高,如果使用复制算法,复制成本会很高。

三、 HotSpot JVM的基石:分代收集(Generational Collection)算法

我们前面提到了三种基本算法,但它们都有各自的优缺点。HotSpot JVM通过分代收集策略,将这些算法巧妙地结合起来,以期达到整体最优的效果。

分代收集是基于一个重要的经验法则——分代假说(Generational Hypothesis)

  1. 弱分代假说(Weak Generational Hypothesis):绝大部分对象都是朝生夕灭的(如局部变量、方法参数等)。
  2. 强分代假说(Strong Generational Hypothesis):熬过越多次垃圾收集过程的对象就越难以消亡,其生命周期会越长。

基于这两个假说,JVM将堆内存划分为两个主要区域:

1. 新生代(Young Generation)
  • 特点:这里存放着新创建的对象。根据弱分代假说,大部分对象在这里很快就会死亡。
  • 构成:通常由一个 Eden 区 和两个 Survivor 区(S0和S1) 组成。
  • 垃圾收集算法:主要采用复制算法。因为存活对象少,复制开销小,且能有效避免内存碎片。
  • GC类型:发生在新生代的GC被称为 Minor GC(或 Young GC)。它的特点是发生频繁、回收速度快、停顿时间短。
  • 对象去向:新创建的对象首先在Eden区分配。当Eden区满时,触发Minor GC。Eden区和其中一个Survivor区(From Space)的存活对象会被复制到另一个空的Survivor区(To Space),同时年龄加1。当对象的年龄达到一定阈值或Survivor区空间不足时,它们会被晋升到老年代。
2. 老年代(Old Generation)
  • 特点:存放经过多次Minor GC仍然存活的对象(即生命周期长的对象),以及一些直接在老年代分配的大对象。老年代的对象存活率高。
  • 垃圾收集算法:通常采用标记-清除标记-整理算法(或两者的混合)。因为存活对象多,如果使用复制算法开销太大;使用标记-整理可以避免内存碎片。
  • GC类型:发生在老年代的GC被称为 Major GC(或 Old GC)。对整个堆(包括新生代和老年代)进行的GC被称为 Full GC。Major GC和Full GC的特点是发生频率低、耗时较长、停顿时间明显。
  • 对象去向:老年代的对象生命周期长,直到它们变得不可达时才会被回收。
分代收集的工作流程(简化版):
  1. 新对象在 Eden区 分配。
  2. 当 Eden 区满时,触发 Minor GC
  3. Eden 区和 From Survivor 区 的存活对象被复制到 To Survivor 区,年龄计数器加1。所有被复制的对象紧密排列。Eden 区和 From Survivor 区被清空。
  4. From 和 To Survivor 区互换角色。
  5. 对象在 Survivor 区中经历多次 Minor GC,年龄逐渐增长。
  6. 当对象年龄达到晋升阈值 (-XX:MaxTenuringThreshold),或 Survivor 区空间不足以容纳时,对象被晋升到 老年代
  7. 当老年代空间不足时,触发 Major GCFull GC

四、 总结与展望

垃圾收集算法是JVM内存管理的核心。从基础的标记-清除、复制、标记-整理,到综合应用这些算法的分代收集,JVM的演进始终围绕着如何更高效、更低停顿地回收内存。

理解这些算法,能帮助我们更好地:

  • 解读GC日志:明白“Young GC”、“Full GC”背后的内存区域和回收机制。
  • 进行JVM参数调优:例如,调整新生代和老年代的比例 (-XX:NewRatio),调整Survivor区比例 (-XX:SurvivorRatio),都是为了让分代收集算法发挥最大效率。
  • 排查性能问题:当发现应用卡顿或内存持续增长时,能够根据GC表现(如Full GC频繁)迅速定位问题可能出在哪个代,从而制定有效的优化策略。

随着硬件技术的发展和应用场景的多样化,现代JVM的垃圾收集器(如G1、ZGC、Shenandoah等)在基本算法的基础上进行了大量的创新与优化,例如区域化分代、并发处理、超低停顿等。它们是分代收集思想的进一步扩展和增强,但其根本依然离不开我们今天讲解的这些基础算法原理。深入学习这些基础,是掌握更高级GC技术不可逾越的桥梁。


文章转载自:

http://5dxAXf8g.jgnjL.cn
http://pGHVbSXv.jgnjL.cn
http://F8S3Wlpu.jgnjL.cn
http://QkDh5yRm.jgnjL.cn
http://GywYo95D.jgnjL.cn
http://6OoUzg1R.jgnjL.cn
http://SHSPC6OQ.jgnjL.cn
http://VKJJZRLj.jgnjL.cn
http://AiNCqbdA.jgnjL.cn
http://Mq8vZM7B.jgnjL.cn
http://2PiUhaa4.jgnjL.cn
http://HuvemWcy.jgnjL.cn
http://Sm6KuDYw.jgnjL.cn
http://hqZedkaf.jgnjL.cn
http://Xas9a92D.jgnjL.cn
http://xhabfQQs.jgnjL.cn
http://SS2mNZJi.jgnjL.cn
http://8EgxCUPH.jgnjL.cn
http://haP9TJbY.jgnjL.cn
http://NxBPhov7.jgnjL.cn
http://n064mNQz.jgnjL.cn
http://neuy1du3.jgnjL.cn
http://5tgR0bqA.jgnjL.cn
http://vEjGZHDJ.jgnjL.cn
http://ULO6jshL.jgnjL.cn
http://C9xloqyy.jgnjL.cn
http://CNEDWUXD.jgnjL.cn
http://YKRR4huo.jgnjL.cn
http://AcmnSksr.jgnjL.cn
http://DcYCFc5f.jgnjL.cn
http://www.dtcms.com/a/384494.html

相关文章:

  • pycharm选择conda的interpreter
  • 为什么要将OpenCV帧转换为PIL图像
  • Apache ShardingSphere 实战:自定义 SQL 拦截插件开发指南
  • 【langchain】加载、处理和分割源数据文件
  • cmake .. -G “Visual Studio 12“
  • i.MX6ULL 外设初始化
  • Node.js如何实现一个WebSocket服务
  • 机器学习shap分析案例
  • Shebang:Node.js 脚本的魔法开头
  • [vue3] 使用reactive声明数组如何正确赋值
  • 微硕MOS管WSF12N15助力汽车电动转向系统
  • SpringBoot快速上手:基础+进阶+项目+源码
  • winscp连接虚拟机centos要求要密码,可是虚拟机登录不需要密码,怎么解决
  • NumPy全面学习笔记
  • Java 轻松实现 Markdown 转 Word、PDF、HTML
  • 时序数据库选型指南:Apache IoTDB企业级解决方案深度解析
  • Java 中 ArrayList 扩容机制的深度解析
  • PowerBI与Excel的区别及实时数据报表开发
  • 【无人机】自检arming参数调整选项
  • Apache Paimon 官方文档
  • CentOS7.9绿色安装apache-tomcat-9.0.109
  • 9款热门局域网文档共享系统横向评测 (2025)
  • 终端安全EDR
  • 【层面一】C#语言基础和核心语法-03(泛型/集合/LINQ)
  • 【连载4】 C# MVC 环境差异化配置:异常处理策略
  • 计算机视觉进阶教学之背景建模与光流估计
  • 铝锆中间合金市场报告:深度解析与未来趋势展望
  • 数据库事务:ACID
  • 动态电源路径管理(DPPM)、NVDC动态路径管理
  • 深入理解链表:从基础概念到经典算法