JVM 垃圾回收机制深度解析(含图解)

news2025/5/22 19:33:45

JVM 垃圾回收机制深度解析(含图解)

一、垃圾回收整体流程

垃圾回收图解

新生代Eden区
Eden区满
达到阈值
未达阈值
老年代空间不足
程序运行
内存分配
对象创建
对象进入Eden区
新生代GC触发条件
Minor GC(新生代回收)
存活对象复制到Survivor区
对象年龄增加
对象晋升到老年代
对象留在Survivor区
继续使用Survivor区
对象进入老年代
老年代GC触发条件
Major GC/Full GC(老年代回收)
标记-清除/整理老年代对象
释放内存

二、垃圾对象判定算法

以下是对 JVM 垃圾回收机制的进一步细化,结合图解直观展示核心概念、算法流程及收集器工作原理:

2.1 引用计数法(Reference Counting)

对象创建
分配内存并初始化引用计数为1
其他对象引用该对象
引用计数+1
引用失效(如指针指向其他对象)
引用计数-1
引用计数是否为0?
对象标记为垃圾,等待回收
对象存活,继续使用
垃圾回收器回收内存
重置内存状态

原理:

  • 计数器管理

    • 每个对象内嵌一个引用计数器(Reference Counter)
    • 引用增加(如被赋值、作为参数传递) → 计数器+1
    • 引用失效(如变量置空、离开作用域) → 计数器-1
  • 回收条件

    • 当计数器归零时,对象立即被标记为垃圾并回收。

循环引用问题: 无法处理循环引用(如对象 A 和 B 互相引用),导致内存泄漏。

A对象
B对象

场景描述:

对象A和B互相引用,导致两者的引用计数始终≥1,即使它们已不被其他对象引用,也无法被回收。

后果:内存泄漏(Memory Leak)。

解决循环引用的常见方法

  • 弱引用(Weak Reference)

    弱引用不增加对象的引用计数(如Java的WeakReference、Python的weakref)。

  • 手动断开引用

    开发者需在代码中显式解除循环引用(易出错,不推荐)。

  • 周期检测算法

    定期运行垃圾回收器检测循环引用(如Python的gc模块)。

2.2 可达性分析(Reachability Analysis)

可达性分析示意图

可达性分析完整流程
三色标记法核心流程
白色
灰色/黑色
所有堆对象初始为白色(隐式状态)
开始可达性分析
标记GC Roots
遍历GC Roots直接引用对象
标记完成
所有白色对象为垃圾
执行垃圾回收
灰色队列是否为空?
标记直接引用对象为灰色
取出一个灰色对象
遍历其引用的子对象
子对象颜色?
标记子对象为灰色并加入队列
跳过处理
当前对象标记为黑色

原理:
从一组称为GC Roots的根对象出发,沿着引用链向下搜索,所有能被访问到的对象视为“存活”,未被访问的则判定为“不可达”对象,可被回收。

整体结构

外层是可达性分析的主流程,内嵌三色标记法的核心流程。

颜色标识统一:

白色:未处理或可回收对象(最终状态)
灰色:待处理对象(中间状态)
黑色 :已处理完成对象
红色边框:垃圾回收相关操作

关键流程节点

  • 标记GC Roots: 从GC Roots(根对象如栈、静态变量等)出发,递归遍历所有可达对象,将其直接引用的堆对象标记为灰色,并加入灰色队列,作为标记的起点。

  • 三色标记循环: 通过灰色队列迭代处理对象(白→灰→黑状态变化)。

    1. 取出灰色对象,遍历其引用的子对象;
    2. 若子对象为白色,标记为灰色并加入队列;
    3. 当前灰色对象标记为黑色;
    4. 重复直到灰色队列为空。”
  • 回收阶段: 清除所有白色(未被标记/不可达)对象,释放内存。

颜色状态映射

存活对象标记流程:
GC Roots → 白色 → 灰色 → 遍历引用 → 黑色(完成)

垃圾对象:
始终为白色 → 最终被回收

可达性分析的优势

  • 解决循环引用问题
    即使对象A和对象B互相引用,若它们无法从GC Roots到达,仍会被判定为垃圾。

  • 高效性
    通过引用链遍历,仅需处理存活对象,避免全堆扫描。

并发标记的挑战
在并发标记阶段(如CMS、G1回收器),用户线程可能修改对象引用,导致两种问题:

  • 漏标(Missing Mark)

    • 增量更新(Incremental Update)

      若黑色对象新增对白色对象的引用,将该黑色对象重新标记为灰色(需重新扫描)。

    • 原始快照(SATB)

      记录标记开始时的对象引用关系,后续新增的引用关系视为“待处理”。

  • 多标(Floating Garbage)

    • 已标记为存活的对象被用户线程置为不可达(通常容忍到下次GC处理)。

GC Roots 包括:
1. 虚拟机栈(Java 栈)中的引用对象
来源: 方法执行时,栈帧中局部变量表存储的引用类型变量。
生命周期: 与方法调用绑定,方法结束后引用失效。
示例:

public void method() {
    Object obj = new Object(); // obj 是 GC Root
    // 方法执行期间,obj 引用的对象不会被回收
} // 方法结束后,obj 出栈,对象失去 GC Root 引用

2. 方法区中静态属性引用的对象
来源: 类的静态变量(static 字段)。
生命周期: 与类的生命周期相同,类卸载前始终存活。
示例:

public class MyClass {
    static Object staticObj = new Object(); // staticObj 是 GC Root
}

3. 方法区中常量引用的对象
来源: 字符串常量池(String Table)、类常量(final static)等。
示例:

public class Constants {
    public static final String NAME = "Doubao"; // "Doubao" 是 GC Root
    public static final List<String> LIST = Collections.unmodifiableList(
        Arrays.asList("a", "b") // 列表对象是 GC Root
    );
}

4. 本地方法栈中 JNI 引用的对象
来源: Java 调用本地代码(如 C/C++)时,本地方法栈中保存的引用。
示例:

public native void nativeMethod(); // 本地方法可能持有对象引用,成为 GC Root

5. 其他特殊引用

  • 活动线程(Active Threads):
Thread thread = new Thread(() -> {
    Object obj = new Object(); // obj 被线程栈引用,成为 GC Root
    // ...
});
thread.start();
  • 类加载器(ClassLoader):
加载的类和静态变量。
  • JVM 内部对象:
如系统类(java.lang.Class)、异常对象(ThreadDeath)等。

GC Roots 内存示意图

GC Roots
引用
引用
引用
引用
栈帧中变量
不可达
对象1
虚拟机栈局部变量
对象2
静态变量
对象3
常量
对象4
本地方法JNI引用
对象5
活动线程
对象6
对象7
对象8
无GC Root引用的对象
垃圾回收

三、垃圾回收算法详解

3.1 标记 - 清除(Mark-Sweep)

开始GC
标记存活对象(蓝色)
清除未标记对象(灰色)
释放内存空间(白色)
产生内存碎片(不连续的绿色区域)

步骤:
1. 标记: 在堆中遍历所有对象,找出内存中需要回收的对象,并且把它们标记出来。

(蓝色为存活对象、灰色为可回收对象、白色为可用内存)
在这里插入图片描述

2. 清除: 清除掉被标记需要回收的对象,释放出对应的内存空间。

在这里插入图片描述

缺点:

  • 效率低: 两次遍历堆,耗时较长。
  • 内存碎片: 回收后产生不连续内存块,可能导致大对象分配失败。

3.2 复制(Copying)

开始GC
将Eden+Survivor区存活对象复制到To Survivor
清空Eden+From Survivor (s0)区
交换From和To Survivor (s1) 区角色
继续使用新的Eden+From区

步骤:

  • 将内存分为Eden 区和两个Survivor 区(通常比例 8:1:1)。
  • 新对象分配到 Eden 区,当 Eden 区满时触发 Minor GC。
  • 存活对象复制到 From Survivor (s0) 区,清空 Eden 区。
  • 下次 GC 时,将 Eden+ From Survivor (s0) 的存活对象复制到 To Survivor (s1) 区,清空原区域。
  • 经历多次 GC 仍存活的对象晋升到老年代。

图解:

  1. 将内存划分为两块相等的区域,每次只使用其中一块

(蓝色为存活对象、灰色为可回收对象、白色为可用内存、绿色为保留内存)
在这里插入图片描述
3. 当其中一块内存使用完了,就将还存活的对象复制到另外一块上面,然后把已经使用过的内存空间一次清除掉。

在这里插入图片描述

优点: 无内存碎片,效率高(只需移动指针)。
缺点: 浪费 50% 内存空间(实际采用 8:1:1 比例,仅浪费 10%)。


3.3 标记 - 整理(Mark-Compact)

开始GC
标记存活对象
将存活对象向一端移动
清理边界外的内存
无内存碎片

步骤:

  • 标记: 遍历标记存活对象(灰色)。
  • 整理: 将存活对象向一端移动,直接清理边界外的内存(白色区域)。
  • 优点: 无内存碎片,适合对象存活率高的场景(如老年代)。

图解:

  1. 标记过程仍然与 标记-清除算法 一样,但是后续步骤不是直接对可回收对象进行清理,
    而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

(蓝色为存活对象、灰色为可回收对象、白色为可用内存)
在这里插入图片描述

  1. 让所有存活的对象都向一端移动,清理掉边界以外的内存。

在这里插入图片描述

缺点: 需移动对象,成本较高。


3.4 分代收集(Generational Collection)

新对象创建
分配到Eden区
Eden区满?
触发Minor GC
继续分配
存活对象复制到From Survivor (s0) 区
清空Eden区
对象年龄+1
年龄达到阈值?
晋升到老年代
对象留在From Survivor (s0) 区
下次Minor GC
存活对象复制到To Survivor (s1) 区
清空Eden+From Survivor (s0) 区
交换Survivor1和To Survivor (s1) 区角色
对象进入老年代
老年代空间不足?
触发Major GC/Full GC
标记-清除/整理老年代对象
释放老年代内存

原理:
根据对象存活周期将堆分为新生代和老年代:

  • 新生代(Eden+Survivor): 对象存活率低,采用 复制算法
  • 老年代: 对象存活率高,采用 标记 - 清除标记 - 整理算法
  • 永久代 / 元空间: 存储类信息、常量池等,GC 频率低。

四、垃圾收集器详解

以下是 Java 主流垃圾回收器的核心启动参数及典型配置
分代收集器分区收集器 分类整理:

I、分代收集器

4.1 Serial 收集器
触发GC
Stop The World
初始标记(标记GC Roots直接引用的对象)
根搜索(递归遍历所有可达对象)
标记所有存活对象
清除未标记对象(回收垃圾)
内存空间整理(可选)
恢复应用线程
继续执行用户程序

特点: 单线程,STW(Stop The World)【全程暂停用户应用程序】,采用复制算法。

算法:

  • 新生代:复制算法(Copying)
    将存活对象从Eden区和Survivor区复制到另一个Survivor区,清除未存活对象。

  • 老年代:标记-整理算法(Mark-Compact)
    标记存活对象后,整理内存空间,消除碎片。

适用场景:

单线程环境(Client 模式),客户端模式或小内存应用。

参数:

# 新生代和老年代均使用Serial(单线程)
-XX:+UseSerialGC -Xmx512m

4.2 Parallel Scavenge 收集器(吞吐量优先)
触发GC
Stop The World
多线程初始标记(标记GC Roots直接引用的对象)
多线程根搜索(并行遍历所有可达对象)
标记所有存活对象
多线程复制存活对象到Survivor区或老年代
清空Eden区和From Survivor区
交换From和To Survivor区角色
恢复应用线程
继续执行用户程序

特点: 多线程,关注吞吐量(运行用户代码时间 / 总时间)。
算法:

  • 新生代(Parallel Scavenge):复制算法
    多线程并行复制存活对象,提升回收效率。

  • 老年代(Parallel Old):标记-整理算法
    多线程并行标记和整理,减少碎片。

适用场景:

多核服务器,注重吞吐量而非低延迟。

参数


# 新生代Parallel Scavenge + 老年代Parallel Old
-XX:+UseParallelGC -XX:+UseParallelOldGC -Xmx4g 

# 控制最大停顿时间(毫秒)
-XX:MaxGCPauseMillis=100 

# 控制吞吐量目标(默认99%)(如 99 表示 1% 时间用于 GC)。
-XX:GCTimeRatio=99

# 自适应调整内存区域大小
-XX:+UseAdaptiveSizePolicy 

4.3 CMS(Concurrent Mark Sweep)收集器(低延迟)
开始CMS GC
初始标记(STW)
并发标记
重新标记(STW)
并发清除
完成GC,恢复用户线程

算法:

  • 标记-清除算法(Mark-Sweep)
  • 工作流程:
    1. 初始标记(STW,短暂):标记 GC Roots 直接关联的对象。
    2. 并发标记:与用户线程并发执行,遍历可达对象。
    3. 重新标记(STW):修正并发标记阶段因用户线程运行导致的对象引用变化(使用增量更新算法)。
    4. 并发清除:与用户线程并发未标记对象。

缺点:

  • 内存碎片: 标记-清除算法不整理内存,长期运行后碎片化严重,可能导致:

    • 晋升失败(Promotion Failed): 新生代对象无法晋升到老年代。

    • 大对象分配失败(Humongous Allocation Failure)。

    • 最终触发 Full GC,使用 Serial Old(标记-整理算法) 压缩内存。

        CMS的核心目标是通过标记-清除算法减少垃圾回收的停顿时间(STW)。
        然而,由于其设计上的限制,CMS在某些场景下触发Full GC,
        无法通过并发操作完成垃圾回收,此时JVM会触发故障回退机制(Fallback Mechanism),
        强制使用单线程的Serial Old收集器(标记-整理算法)
        进行压缩执行Full GC以恢复内存可用性。
        这种设计是CMS在低延迟与内存连续性之间的权衡。
      
  • 吞吐量下降: 并发阶段占用 CPU 资源,与用户线程竞争。

  • 无法处理突发分配压力: 并发周期中若老年代空间不足,触发并发模式失败(Concurrent Mode Failure)。

触发Full GC的场景描述是否使用Serial Old
并发模式失败并发标记期间老年代空间不足
晋升失败新生代对象晋升到老年代时因碎片无法分配
显式调用System.gc()强制触发Full GC(默认开启ExplicitGCInvokesConcurrent可绕过此行为)
元空间耗尽类元数据区无法扩展
其他内存分配失败直接内存(Direct Memory)或本地内存(Native Memory)耗尽等
触发条件: 并发模式失败/晋升失败等
STW: 终止并发操作
启动Serial Old收集器
标记阶段: 标记存活对象
整理阶段: 移动对象消除碎片
清除阶段: 回收垃圾内存
恢复用户线程

适用场景

Web 服务器、需要快速响应的应用、老年代垃圾回收、追求低停顿时间。

参数

# 新生代ParNew + 老年代CMS
-XX:+UseConcMarkSweepGC -Xmx8g 

# 老年代使用70%时触发CMS(默认92%)
-XX:CMSInitiatingOccupancyFraction=70

# 控制Full GC时是否进行内存压缩(减少碎片)(标记-整理)。
-XX:+UseCMSCompactAtFullCollection

# 并行执行重新标记阶段
-XX:+CMSParallelRemarkEnabled

# 指定经过多少次Full GC后执行一次压缩(0表示每次Full GC都压缩)。
-XX:CMSFullGCsBeforeCompaction=0    

II、分区收集器

4.4 G1(Garbage-First)收集器(区域化、平衡延迟与吞吐量)
开始G1 GC
初始标记(STW)
并发标记
最终标记(STW)
筛选回收(STW)
根据停顿时间选择Region回收
复制存活对象到新Region
清空回收的Region
继续执行用户线程

算法:

  • 分区(Region)
    将堆划分为多个大小相等的 Region(如 2MB-32MB)。

  • 新生代:复制算法
    存活对象复制到Survivor Region或晋升到老年代Region。

  • 老年代:标记-整理算法
    并发标记后,优先回收垃圾最多的Region(Garbage-First),并整理Region内空间。

  • 动态分代
    每个 Region 可动态扮演 Eden、Survivor、Old 或 Humongous(大对象)。

  • 可预测停顿
    根据 Region 的回收价值(垃圾占比)排序,优先回收收益高的区域。

  • 工作流程
    1. 初始标记(STW 极短): 标记 GC Roots 直接关联的 Region。
    2. 并发标记: 与用户线程并发标记存活对象。
    3. 最终标记(STW): 处理并发阶段的增量更新(使用 SATB 快照算法)。
    4. 筛选回收(STW): 根据停顿时间目标选择 Region 进行回收,采用复制算法。

关键说明:
G1 设计上通过 Mixed GC(混合回收) 避免 Full GC,但在极端情况(如并发标记失败)下仍可能触发 Full GC,此时 触发故障回退机制(Fallback Mechanism) 到单线程的 Serial Old收集器(标记-整理算法)

  • 触发条件: 堆内存不足且无法通过 Mixed GC 回收足够空间。
  • 优化建议: 通过 -XX:G1ReservePercent 增加预留内存,降低 Full GC 概率。

适用场景:

大堆内存(数十GB至数百GB)、追求低延迟与吞吐量平衡的应用。

参数:

# 启用G1收集器(JDK 9+默认)
-XX:+UseG1GC -Xmx16g

# 设置最大停顿时间目标(毫秒)
-XX:MaxGCPauseMillis=200

# 设置Java堆区域大小(默认2的幂,1-32MB)
-XX:G1HeapRegionSize=8m

# 老年代占用50%时启动混合回收
-XX:InitiatingHeapOccupancyPercent=50
#  G1 垃圾收集器预留堆内存的百分比,默认值:10(即预留 10% 的堆内存)
-XX:G1ReservePercent=10

4.5 ZGC收集器(Z Garbage Collector)(超低延迟,JDK 11+)
开始ZGC
初始标记(极短STW)
并发标记
再标记(极短STW)
并发转移准备
并发转移(通过读屏障修正引用)
完成GC,几乎无STW

特点:

  • 着色指针(Colored Pointers)
    将 GC 信息(Marked0/1、Remapped)存储在指针的低 4 位,无需扫描对象头。

  • 读屏障(Load Barrier)
    在读取对象引用时动态修正指针,实现并发移动。

  • 并发整理
    全程几乎无 STW(仅初始标记和再标记有极短停顿,通常 < 1ms)。

适用场景:

超大堆内存(TB 级),追求极致低延迟(<10ms),如金融交易系统。

算法:

  • 并发标记-整理算法(Concurrent Mark-Compact)

    1. 并发标记: 利用染色指针(Colored Pointers)标记对象状态。

    2. 并发预备重分配: 确定需要清理的内存区域。

    3. 并发重分配: 将存活对象复制到新地址,并更新引用。

    4. 并发重映射: 修复旧地址到新地址的映射。

    5. 关键技术: 染色指针和读屏障,支持全阶段并发执行。

参数:

# 启用ZGC(需JDK 11+)
-XX:+UseZGC -Xmx32g 

# 设置最大堆大小(ZGC适合大内存)
-Xmx64g -Xms64g

# 设置并发GC线程数(默认CPU核心数的1/4)
-XX:ConcGCThreads=4

# 配置GC周期的最大停顿时间(目标值)
-XX:MaxGCPauseMillis=10

4.6 Shenandoah收集器(超低延迟,OpenJDK 12+)
触发GC
初始标记(STW)
并发标记
最终标记(STW)
并发回收准备
并发对象转移
引用修正(读屏障)
并发清理
完成GC,恢复应用线程

特点:

  • 与 ZGC 类似,基于转发指针和布鲁姆过滤器实现并发回收,停顿时间极短。

适用场景:

OpenJDK 环境下的超大堆内存(TB级)、极低停顿时间(亚毫秒级)、低延迟应用。

算法:

  • 并发复制算法(Concurrent Copying)

    1. 并发标记: 与用户线程并发标记存活对象。

    2. 并发预清理: 确定需要回收的Region。

    3. 并发转移: 在用户线程运行时,将存活对象复制到新Region。

    4. 引用更新: 并发更新对象引用至新地址。

    5. 关键技术: 使用 读屏障(Read Barrier) 实现并发对象移动。

参数:

# 启用Shenandoah(需JDK 12+,OpenJDK默认包含)
-XX:+UseShenandoahGC -Xmx16g 

# 设置GC策略(normal/size/reference-time)
-XX:ShenandoahGCHeuristics=normal

# 设置最大停顿时间目标(毫秒)
-XX:MaxGCPauseMillis=100

III、垃圾收集器对比表

收集器新生代算法老年代算法Full GC 收集器JDK版本支持默认收集器(JDK版本)推荐堆大小吞吐量适用场景是否开源所属JVM实现垃圾对象判定算法核心特点
Serial复制标记-整理Serial Old(标记-整理)JDK 1.0+JDK 1.0~8(客户端模式)<512MB客户端应用、嵌入式系统HotSpot可达性分析(迭代标记)单线程简单高效,无多线程开销
Parallel复制标记-整理Parallel Old(标记-整理)JDK 1.4+JDK 8(服务器模式)512MB~32GB批处理、计算密集型任务HotSpot可达性分析(并行标记)多线程并行回收,最大化吞吐量
CMS复制(ParNew)标记-清除Serial Old(标记-整理)JDK 1.5~JDK14 (JDK 9+ 标记为废弃,JDK 14+ 移除)无(需手动启用)512MB~4GB低延迟Web服务(中小堆)HotSpot可达性分析(三色标记 + 增量更新)低延迟,并发标记减少停顿,但需监控碎片问题
G1复制标记-整理(分Region)Serial Old(标记-整理)JDK 7u4+JDK 9+(服务器模式)4GB~64GB中~高大堆内存、平衡型应用HotSpot可达性分析(三色标记 + SATB快照)分区回收,可预测停顿,自动整理碎片
ZGC并发复制并发标记-整理无(无 Full GC 概念)OpenJDK 11+JDK 15+(可选)≥4GB超大堆、亚毫秒级停顿HotSpot可达性分析(并发三色标记 + 染色指针)超大堆,染色指针,停顿<1ms
Shenandoah并发复制并发复制无(无 Full GC 概念)OpenJDK 12+无(需手动启用)≥4GB超大堆、极低延迟需求HotSpot可达性分析(并发三色标记 + 读屏障)全堆并发,停顿<10ms

IIII、垃圾对象判定算法详解

收集器判定算法实现
Serial递归遍历GC Roots,标记可达对象(无并发优化)。
Parallel多线程并行遍历GC Roots,标记可达对象。
CMS三色标记法 + 增量更新(将修改引用的黑色对象重新标记为灰色)。
G1三色标记法 + SATB(原始快照)(基于标记开始时的引用关系进行标记)。
ZGC并发三色标记法 + 染色指针(通过指针元数据记录对象状态)。
Shenandoah并发三色标记法 + 读屏障(拦截对象访问,处理并发标记中的引用变化)。

五、GC 日志分析与可视化工具

获取GC日志
分析GC类型(Minor/Full)
提取停顿时间
计算吞吐量
分析内存使用趋势
定位频繁GC原因
确定是否存在内存泄漏
调整堆大小/收集器参数
验证优化效果

5.1 典型 GC 日志示例

[GC (Allocation Failure) [PSYoungGen: 524288K->1024K(524288K)] 524288K->1025K(1048576K), 0.0012345 secs]

解析:

  • GC:新生代 GC。
  • Full GC:整堆 GC。
  • Allocation Failure:因内存分配失败触发 GC。
  • PSYoungGen:Parallel Scavenge 收集器的新生代。
  • 524288K->1024K:回收前→回收后的内存使用量。
  • 0.0012345 secs:GC 耗时。

5.2 可视化工具推荐

  • GCViewer:解析 GC 日志,生成可视化图表(如停顿时间、内存使用趋势)。
  • GCEasy:在线工具,上传 GC 日志后提供详细分析报告和优化建议。
  • JProfiler:集成 GC 分析功能,支持实时监控和堆转储分析。
  • Java Mission Control (JMC):Oracle 官方工具,提供高性能监控和诊断。
  • Arthas(阿尔萨斯):阿里巴巴开源的 实时诊断工具,通过命令行或 Web UI 动态监控和排查问题。
  • VisualVM:轻量级可视化工具,集成多种性能分析功能(含 GC 监控),支持插件扩展。

六、性能调优实战案例

6.1 高并发 Web 应用调优

场景: 8 核 16GB 服务器,Tomcat 应用,响应时间敏感。
配置:

java -Xms8g -Xmx8g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=100 \
-XX:G1HeapRegionSize=8m \
-XX:+ParallelRefProcEnabled \
-jar app.jar

效果:

  • 新生代 GC 频率降低,停顿时间控制在 50ms 以内。
  • 老年代碎片化问题缓解,Full GC 频率从每天数次降为每周一次。

6.2 大数据批处理调优

场景: Spark 作业,64GB 内存,追求高吞吐量。
配置:

spark-submit --conf spark.executor.memory=48g \
--conf spark.executor.extraJavaOptions="-XX:+UseParallelGC -XX:GCTimeRatio=99" \
--class com.example.BatchJob app.jar

效果:

  • 吞吐量提升 20%,GC 时间占比从 5% 降至 1%。
  • 并行 GC 充分利用多核 CPU,减少 STW 时间。

七、总结与最佳实践

垃圾判定:优先使用可达性分析,避免引用计数法的循环引用问题。
算法选择

  • 新生代:对象存活率低→复制算法。
  • 老年代:对象存活率高→标记 - 整理算法。

收集器选型

<=4GB
4GB-16GB
高吞吐量
低延迟
>16GB
高吞吐量
低延迟
选择垃圾收集器
堆内存大小
Serial/Parallel
延迟要求
Parallel+Parallel Old
CMS/G1
延迟要求
Parallel+Parallel Old
是否需要商业支持
ZGC
Shenandoah
CMS(老年代)+ ParNew(新生代)
G1
适用于老年代对象存活率高、允许一定碎片的场景
适用于大内存、追求平衡的场景
  • 小内存 / 单线程:Serial。
  • 吞吐量优先:Parallel。
  • 低延迟:CMS(老年代)、G1(大内存)、ZGC/Shenandoah(超大内存)。

性能监控:

  • 定期分析 GC 日志,监控停顿时间和内存使用趋势。
  • 使用可视化工具定位内存泄漏和大对象问题。

通过结合图解和实战案例,可更直观地理解 JVM 垃圾回收机制的底层原理,从而针对性地优化应用性能。

附:

  • JVM图解
本地方法接口
执行引擎
Java虚拟机运行时数据区
类加载子系统
垃圾回收器
方法区(线程共享)
堆内存(线程共享)
虚拟机栈(线程私有)
程序计数器(线程私有)
链接(Linking)
启动类加载器
扩展类加载器
系统类加载器
对象分配
对象分配
晋升
晋升
本地方法库
JNI
操作系统资源
逐行解释字节码
解释器
中间代码生成器
即时编译器
代码优化器
目标代码生成器
新生代GC
GC管理器
老年代GC
垃圾回收器分类
Serial
新生代收集器
ParNew
Parallel Scavenge
老年代收集器
Serial Old
Parallel Old
CMS
混合收集器
G1
ZGC
Shenandoah
本地方法存储
本地方法栈(线程私有)
线程A计数器
线程B计数器
栈帧
线程A
局部变量表
操作数栈
动态链接
方法返回地址
附加信息
栈帧
线程B
Eden
新生代
Survivor0
Survivor1
老年代
类元信息
方法元信息
运行时常量池
静态变量
加载
.class文件
初始化
准备
验证
解析
  • ⚙️运行时数据区的初始化时机
运行时数据区初始化时机与类加载的关系
方法区JVM 启动时分配内存,类加载时写入类元信息、常量池、静态变量等。类加载的初始化阶段会向方法区写入静态变量。
JVM 启动时分配内存,对象实例在 new 时创建。类初始化完成后,通过 new 触发对象分配。
虚拟机栈线程创建时初始化,每个线程有独立栈空间,栈帧在方法调用时动态生成。与类加载无关,由线程执行引擎管理。
程序计数器线程创建时初始化,记录当前执行指令地址。与类加载无关,由线程执行引擎管理。
本地方法栈线程创建时初始化,存储本地方法(Native Method)调用信息。与类加载无关,由本地方法接口(JNI)管理。
  • 类加载与运行时数据区的关系
运行时数据区
类加载子系统
写入
触发对象创建
方法调用时创建栈帧
虚拟机栈
本地方法栈
程序计数器
初始化
链接
加载
方法区
栈帧
局部变量表
操作数栈

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2383340.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何利用 Conda 安装 Pytorch 教程 ?

如何利用 Conda 安装 Pytorch 教程 &#xff1f; 总共分为六步走&#xff1a; &#xff08;1&#xff09;第一步&#xff1a;验证conda 环境是否安装好&#xff1f; 1) conda -V2) conda --version&#xff08;2&#xff09;第二步&#xff1a;查看现有环境 conda env list…

uniapp vue 开发微信小程序 分包梳理经验总结

嗨&#xff0c;我是小路。今天主要和大家分享的主题是“uniapp vue 开发微信小程序 分包梳理经验总结”。 在使用 UniAppvue框架开发微信小程序时&#xff0c;当项目比较大的时候&#xff0c;经常需要分包加载。它有助于控制主包的大小&#xff0c;从而提升小程序的启…

什么是VR展示?VR展示的用途

随着科技的迅猛发展&#xff0c;我们步入一个全新的数字时代。在这个时代&#xff0c;虚拟现实&#xff08;VR&#xff09;技术崭露头角&#xff0c;逐步改变我们对世界的认知。全景展示厅作为VR技术与传统展览艺术的完美结合&#xff0c;以独特的全景视角&#xff0c;引领我们…

.NET外挂系列:4. harmony 中补丁参数的有趣玩法(上)

一&#xff1a;背景 1. 讲故事 前面几篇我们说完了 harmony 的几个注入点&#xff0c;这篇我们聚焦注入点可接收的几类参数的解读&#xff0c;非常有意思&#xff0c;在.NET高级调试 视角下也是非常重要的&#xff0c;到底是哪些参数&#xff0c;用一张表格整理如下&#xff…

Go语言中new与make的深度解析

在 Go 语言中&#xff0c;new 和 make 是两个用于内存分配的内置函数&#xff0c;但它们的作用和使用场景有显著区别。 理解它们的核心在于&#xff1a; new(T): 为类型 T 分配内存&#xff0c;并将其初始化为零值&#xff0c;然后返回一个指向该内存的指针 (*T)。make(T, ar…

3、ubantu系统 | 通过vscode远程安装并配置anaconda

1、vscode登录 登录后通过pwd可以发现目前位于wangqinag账号下&#xff0c;左侧为属于该账号的文件夹及文件。 通过cd ..可以回到上一级目录&#xff0c;通过ls可以查看当前目录下的文件夹及文件。 2、安装 2.1、下载anaconda 通过wget和curl下载未成功&#xff0c;使用手动…

【Unity】 HTFramework框架(六十五)ScrollList滚动数据列表

更新日期&#xff1a;2025年5月16日。 Github 仓库&#xff1a;https://github.com/SaiTingHu/HTFramework Gitee 仓库&#xff1a;https://gitee.com/SaiTingHu/HTFramework 索引 一、ScrollList滚动数据列表二、使用ScrollList1.快捷创建ScrollList2.ScrollList的属性3.自定义…

Swagger在java的运用

Swagger 是一个广泛使用的工具&#xff0c;用于设计、构建、记录和使用 RESTful Web 服务。它通过提供交互式的 API 文档、客户端 SDK 生成和 API 发现功能&#xff0c;极大地简化了 API 的开发和使用过程。以下是对 Swagger 的详细介绍&#xff0c;包括它的功能、使用场景、如…

代码随想录算法训练营 Day49 图论Ⅰ 深度优先与广度优先

图论 基础 图的概念 图的概念 概念清单有向图 (a)无向图 (b)有向/无向如图 a 所示每条边有指向如图 b 所示每条边没有箭头指向权值每条边的权值每条边的权值度-有几条边连到该节点 (eg V 2 V_2 V2​ 度为 3)入度/出度出度&#xff1a;从该节点出发的边个数入度&#xff1a;…

.NET外挂系列:1. harmony 基本原理和骨架分析

一&#xff1a;背景 1. 讲故事 为什么要开这么一个系列&#xff0c;是因为他可以对 .NET SDK 中的方法进行外挂&#xff0c;这种技术对解决程序的一些疑难杂症特别有用&#xff0c;在.NET高级调试 领域下大显神威&#xff0c;在我的训练营里也是花了一些篇幅来说这个&#xf…

HarmonyOS NEXT端云一体化工程目录结构

视频课程学习报名入口:HarmonyOS NEXT端云一体化开发 端云一体化开发工程由端开发工程(Application)和云开发工程(CloudProgram)两大核心模块构成。 1)端开发工程目录结构 端开发工程主要用于开发应用端侧的业务代码,通用云开发模板的端开发工程目录结构如下图所示: …

Ajax研究

简介 AJAX Asynchronous JavaScript and XML&#xff08;异步的 JavaScript 和 XML&#xff09;。 AJAX 是一种在无需重新加载整个网页的情况下&#xff0c;能够更新部分网页的技术。 Ajax 不是一种新的编程语言&#xff0c;而是一种用于创建更好更快以及交互性更强的Web应用…

学习 Android(十)Fragment的生命周期

简介 Android 的 Fragment 是一个具有自己生命周期的 可重用 UI 组件&#xff0c;能够在运行时灵活地添加、移除和替换&#xff0c;从而支持单 Activity 多界面、动态布局和响应式设计。掌握 Fragment 的生命周期有助于正确地在各个阶段执行初始化、资源绑定、状态保存与释放操…

RT Thread FinSH(msh)调度逻辑

文章目录 概要FinSH功能FinSH调度逻辑细节小结 概要 RT-Thread&#xff08;Real-Time Thread&#xff09;作为一款开源的嵌入式实时操作系统&#xff0c;在嵌入式设备领域得到了广泛应用。 该系统不仅具备强大的任务调度功能&#xff0c;还集成了 FinSH命令行系统&#xff0c…

单片机设计_四轴飞行器(STM32)

四轴飞行器&#xff08;STM32&#xff09; 想要更多项目私wo!!! 一、系统简介 四轴飞行器是一种通过四个旋翼产生的升力实现飞行的无人机&#xff0c;其核心控制原理基于欧拉角动力学模型。四轴飞行器通过改变四个电机的转速来实现六自由度控制&#xff08;前后、左右、上下…

【Spring Boot】配置实战指南:Properties与YML的深度对比与最佳实践

目录 1.前言 2.正文 2.1配置文件的格式 2.2properties 2.2.1基础语法 2.2.2value读取配置文件 2.2.3缺点 2.3yml 2.3.1基础语法 2.3.2配置不同数据类型 2.3.3配置读取 2.3.4配置对象和集合 2.3.5优缺点 2.4综合练习&#xff1a;验证码案例 2.4.1分析需求 2.4.2…

算法优选系列(9.BFS 解决拓扑排序)

目录 拓扑排序简介&#xff1a; ​编辑 课程表&#xff08;medium&#xff09;&#xff1a; 课程表II&#xff08;medium&#xff09;: 火星词典&#xff08;hard&#xff09;&#xff1a; 拓扑排序简介&#xff1a; 有向无环图&#xff08;DAG图&#xff09; 如上图每条边…

【Java高阶面经:微服务篇】7. 1秒响应保障:超时控制如何成为高并发系统的“救火队长”?

一、全链路超时建模:从用户需求到系统分解 1.1 端到端时间预算分配 黄金公式: 用户期望响应时间 = 网络传输时间 + 服务处理时间 + 下游调用时间 + 缓冲时间典型分配策略(以1秒目标为例): 环节时间预算优化目标客户端渲染100ms骨架屏(Skeleton)预渲染边缘节点(CDN)…

力扣周赛置换环的应用,最少交换次数

置换环的基本概念 置换环是排列组合中的一个概念&#xff0c;用于描述数组元素的重排过程。当我们需要将一个数组转换为另一个数组时&#xff0c;可以把这个转换过程分解为若干个 “环”。每个环代表一组元素的循环交换路径。 举个简单例子 假设原数组 A [3, 2, 1, 4]&…

大语言模型 12 - 从0开始训练GPT 0.25B参数量 MiniMind2 补充 训练开销 训练步骤 知识蒸馏 LoRA等

写在前面 GPT&#xff08;Generative Pre-trained Transformer&#xff09;是目前最广泛应用的大语言模型架构之一&#xff0c;其强大的自然语言理解与生成能力背后&#xff0c;是一个庞大而精细的训练流程。本文将从宏观到微观&#xff0c;系统讲解GPT的训练过程&#xff0c;…