JVM 垃圾收集算法详解!

目录

      • [一、 内存管理的挑战与垃圾收集的必要性](#一、 内存管理的挑战与垃圾收集的必要性)
      • [二、 基础垃圾收集算法](#二、 基础垃圾收集算法)
        • [1. 标记-清除(Mark-Sweep)算法](#1. 标记-清除(Mark-Sweep)算法)
        • [2. 复制(Copying)算法](#2. 复制(Copying)算法)
        • [3. 标记-整理(Mark-Compact)算法](#3. 标记-整理(Mark-Compact)算法)
      • [三、 HotSpot JVM的基石:分代收集(Generational Collection)算法](#三、 HotSpot JVM的基石:分代收集(Generational Collection)算法)
      • [四、 总结与展望](#四、 总结与展望)

摘要:在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技术不可逾越的桥梁。

相关推荐
罗西的思考2 小时前
【OpenClaw具身硬件】MiniClaw 阅读笔记---(1)基础
人工智能·算法·机器学习
蛋先生DX2 小时前
大模型参数存储格式揭秘:BF不是男朋友
深度学习·算法·llm
爱跳舞的烤冷面3 小时前
自学嵌入式第22天(数据结构——哈希)
数据结构·算法·哈希算法
猎嘤一号3 小时前
博弈论(Game Theory)的理论、算法与工程
人工智能·算法·安全·博弈论
月光船幽幽3 小时前
影子模式下保护 logits 不被修改
人工智能·python·算法
马拉AI3 小时前
腾讯开源 Agent 记忆系统,AI“换对话就忘”的问题有了新解法(附安装使用教程)
人工智能·算法·开源·科研
地平线开发者5 小时前
征程6工具链模型X86推理方式说明
算法
地平线开发者5 小时前
【征程6】校准量化中HistogramObserver解析
算法
OuO-26 小时前
笔试强训 Day 34:ISBN 号码、kotori 和迷宫、矩阵最长递增路径
java·算法·矩阵
程序喵大人7 小时前
【C++进阶】STL算法与函数对象 - 04 find、count和any_of把查询写成意图
开发语言·c++·算法