万字长文,说清JVM垃圾回收,让内存管理不再黑盒

作为一名在互联网大厂摸爬滚打十余年的架构师,我深知JVM垃圾回收这个话题对新手的"杀伤力",第一次接触时我也是一脸懵圈,甚至怀疑自己是不是不适合学Java,但坚持下来才发现这些知识构成了我技术实力的重要基石,今天就让我们一起来揭开JVM垃圾回收器的神秘面纱,从小白到大神,一文搞定,

一,为什么需要垃圾回收

记得我刚学编程时,写的是C语言,那时候申请内存和释放内存都需要手动操作,稍不注意就会出现内存泄漏或者野指针问题,简直噩梦,忘记释放一块内存,你的程序可能会慢慢吃掉所有系统资源,最后崩溃,

这就是为什么Java的自动垃圾回收机制如此受欢迎,它让开发者可以专注于业务逻辑实现,而不用考虑内存管理这种底层问题,JVM会自动回收不再使用的对象,释放内存空间,极大提高了开发效率和程序稳定性,

垃圾回收的本质就是找出程序不再使用的对象,并释放它们占用的内存空间,听起来简单,但实现起来相当复杂,不同的垃圾回收器各有特点,适用于不同的应用场景,

二,JVM内存模型基础

在讲垃圾回收器之前,我们先简单回顾一下JVM内存模型,因为垃圾回收器主要工作在堆内存区域,
在这里插入图片描述

JVM内存主要分为以下几个区域:

  1. 堆内存(Heap):存储对象实例,是垃圾回收的主要区域,
  2. 方法区:存储类信息,常量,静态变量等,
  3. 虚拟机栈:线程私有,存储局部变量表等,
  4. 本地方法栈:线程私有,执行本地方法,
  5. 程序计数器:线程私有,记录当前线程执行的位置,

其中,垃圾回收主要工作在堆内存区域,堆内存又分为新生代和老年代,新生代又细分为Eden区和两个Survivor区(通常称为S0和S1或From和To),

三,垃圾回收的基本原理

3.1 如何判断对象是否可回收

在垃圾回收的世界里,首先要解决的问题是:哪些对象是垃圾,我们来看两种主要的判断方法,

引用计数法

最直观的方法是引用计数法:每个对象有一个引用计数器,当有引用指向它时,计数器加1,当引用失效时,计数器减1,当计数器为0时,表示对象不再被使用,可以回收,

这种方法实现简单,判定效率高,但有个致命问题:无法解决循环引用,比如对象A引用对象B,对象B引用对象A,尽管外部没有引用指向A和B,但它们的引用计数都不为0,导致无法回收,

可达性分析

因此,JVM采用可达性分析算法:以一系列"GC Roots"对象作为起点,沿着引用链向下搜索,如果一个对象到GC Roots没有任何引用链相连,则证明该对象不可达,可以回收,
在这里插入图片描述

GC Roots主要包括:

  1. 虚拟机栈中引用的对象,
  2. 方法区中类静态属性引用的对象,
  3. 方法区中常量引用的对象,
  4. 本地方法栈中JNI引用的对象,

在上图中,对象E和对象F虽然互相引用,形成了循环引用,但它们都无法从GC Roots到达,因此会被判定为垃圾对象,这也是可达性分析算法优于引用计数法的地方,

3.2 垃圾回收算法

判断完对象是否可回收后,下一步就是如何高效地回收这些垃圾对象,几种常见的垃圾回收算法如下:

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

最基础的垃圾回收算法,分为两个阶段:

  1. 标记阶段:标记出所有需要回收的对象,
  2. 清除阶段:清除所有被标记的对象,

这种算法的缺点非常明显:一是效率不高,标记和清除两个过程都比较耗时,二是会产生大量不连续的内存碎片,导致无法分配大对象时提前触发另一次垃圾回收,

复制算法(Copying)

为了解决标记-清除算法的效率问题,复制算法将内存分为两块,每次只使用其中一块,当这一块用完了,就将还存活的对象复制到另一块上,然后一次性清理掉已使用过的内存空间,

这种算法实现简单,运行高效,但代价是内存利用率只有一半,所以通常用于新生代垃圾回收,新生代对象存活率较低,复制成本小,

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

标记-整理算法是针对老年代对象存活率高的特点,在标记-清除算法基础上做了改进,标记过程与标记-清除算法一样,但后续不是直接清理对象,而是将所有存活的对象向内存空间一端移动,然后清理掉边界以外的内存,

这种算法避免了内存碎片问题,也不会像复制算法那样浪费内存空间,但移动对象的过程会增加额外开销,

分代收集算法(Generational Collection)

分代收集算法并不是一种具体的算法,而是根据对象的生命周期将内存划分为几块,一般是新生代和老年代,然后根据各个年代的特点采用最适当的收集算法,

通常,新生代使用复制算法,老年代使用标记-整理或标记-清除算法,这也是目前虚拟机使用的算法,

四,JVM垃圾回收器详解

了解了基本原理,我们来看看JVM中实际使用的垃圾回收器,我会按照时间顺序介绍主流的垃圾回收器,包括它们的特点,优缺点及适用场景,

4.1 Serial收集器 - 单线程的稚嫩收集器

Serial收集器是最古老,最简单的收集器,单线程工作,在回收时必须暂停所有用户线程(Stop The World),对于新生代采用复制算法,对老年代采用标记-整理算法,
在这里插入图片描述

优点:

  1. 简单高效,没有线程交互的开销,
  2. 在单CPU环境下表现良好,

缺点:

  1. 需要Stop The World,暂停所有用户线程,
  2. 无法利用多核CPU优势,

适用场景:

  1. 客户端应用,如桌面程序,
  2. 内存较小,CPU核心数少的场景,

虽然看起来很原始,但Serial收集器依然是虚拟机Client模式下的默认新生代收集器,在单核或者内存受限的环境中仍有一席之地,

4.2 ParNew收集器 - Serial的多线程版本

ParNew收集器实际上是Serial收集器的多线程版本,除了使用多线程进行垃圾回收外,其余行为包括算法,STW,对象分配规则等都与Serial收集器一样,

在这里插入图片描述

优点:

  1. 多线程收集,在多核CPU环境下有更好的表现,
  2. 与CMS收集器搭配使用效果好,

缺点:

  1. 暂停用户线程(STW),影响应用响应时间,
  2. 在单核CPU环境中表现不如Serial收集器,

适用场景:

  1. 多核CPU服务器环境,
  2. 对吞吐量有一定要求的中小型应用,

多线程垃圾回收是趋势,ParNew收集器是许多运行在Server模式下JVM的首选新生代收集器,特别是搭配CMS收集器时,

4.3 Parallel Scavenge收集器 - 关注吞吐量的收集器

Parallel Scavenge收集器也是一个新生代收集器,同样基于复制算法实现,也是并行的多线程收集器,看起来和ParNew收集器
ZGC的几个显著特点:

  1. 低延迟:STW时间不会随着堆大小或存活对象增加而增加,始终保持在10ms以内,
  2. 大内存支持:可以高效处理从几百MB到几TB的堆内存,
  3. 着色指针:在对象指针中存储标记信息,而不是在对象头中,
  4. 读屏障:在程序读取对象引用时,使用读屏障技术确保引用的有效性,

ZGC只在JDK 11及以上版本支持,并在JDK 15进入生产就绪状态,它主要在Linux/x64平台上工作最佳,

优点:

  1. 超低延迟,最大停顿时间通常在10ms以内,
  2. 可扩展到TB级别的堆,且停顿时间不会随堆大小增加,
  3. 与G1相比有更高的吞吐量,

缺点:

  1. 吞吐量略低于Parallel GC,
  2. 支持平台有限制,对CPU架构有要求,

适用场景:

  1. 对延迟极其敏感的应用,如实时交易系统,
  2. 超大堆内存应用,

在一个金融交易系统中,我们使用ZGC成功将GC停顿时间从之前的50-100ms降到了稳定的5ms以内,极大改善了用户体验,

Parallel Scavenge收集器也是一个新生代收集器,同样基于复制算法实现,也是并行的多线程收集器,看起来和ParNew收集器很像,但它的关注点与其他收集器不同,Parallel Scavenge收集器的目标是达到一个可控制的吞吐量,

什么是吞吐量,这里简单解释一下:吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间),比如程序运行了100分钟,其中垃圾收集花费1分钟,那么吞吐量就是99%,

Parallel Scavenge提供了两个重要参数:

  1. -XX:MaxGCPauseMillis:控制最大垃圾收集停顿时间,
  2. -XX:GCTimeRatio:设置吞吐量大小,

此外,Parallel Scavenge还有一个特点是支持自适应调节策略,通过参数-XX:+UseAdaptiveSizePolicy开启,让虚拟机自己调整新生代大小,Eden与Survivor比例,晋升老年代对象年龄等参数,无需手工指定,

优点:

  1. 并行多线程收集,吞吐量高,
  2. 自适应调节策略,易于使用,

缺点:

  1. 仍然有STW问题,不适合对响应时间有严格要求的应用,

适用场景:

  1. 后台运算而不需要太多交互的任务,如批量处理,
  2. 科学计算等计算密集型应用,

Parallel Scavenge在JDK1.8中是server模式下的默认垃圾收集器,特别适合需要高吞吐量而不太关心停顿时间的场景,

4.4 Serial Old收集器 - 老年代单线程收集器

Serial Old是Serial收集器的老年代版本,同样是单线程收集器,使用标记-整理算法,主要用于Client模式下的老年代收集,

在Server模式下,它还有两个用途:一是作为CMS收集器的后备方案,在并发收集发生Concurrent Mode Failure时使用,二是在JDK1.5及之前版本中与Parallel Scavenge收集器搭配使用,

由于是单线程设计,Serial Old在Server模式下性能较弱,已经很少使用,

4.5 Parallel Old收集器 - 老年代并行收集器

Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和标记-整理算法,在JDK1.6中才开始提供,

在这里插入图片描述

在Parallel Old出现之前,Parallel Scavenge收集器只能与Serial Old收集器配合使用,由于老年代Serial Old是单线程的,无法充分发挥出多核优势,所以在JDK1.6推出了Parallel Old收集器,与Parallel Scavenge收集器配合,可以充分利用多核CPU的优势,

优点:

  1. 并行多线程收集,吞吐量高,
  2. 与Parallel Scavenge搭配使用,可以充分发挥服务器多核性能,

缺点:

  1. 仍然存在STW问题,

适用场景:

  1. 注重吞吐量的服务器应用,
  2. 可以接受较长停顿时间的批处理任务,

4.6 CMS收集器 - 低延迟的收集器

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,非常适合对响应时间有要求的应用,如互联网站或者B/S系统的服务端,

在这里插入图片描述

CMS收集器的工作过程相对复杂,分为四个主要阶段:

  1. 初始标记(STW):仅标记GC Roots直接关联的对象,速度很快,需要STW,
  2. 并发标记:从GC Roots开始对堆中对象进行可达性分析,耗时较长,但与用户线程并发执行,不需要STW,
  3. 重新标记(STW):修正并发标记期间因用户程序继续运行而导致的标记变动,需要STW,时间比初始标记长,但远比并发标记短,
  4. 并发清除:清除已死亡对象,与用户线程并发执行,

优点:

  1. 并发收集,低停顿,
  2. 对响应时间要求高的应用友好,

缺点:

  1. 对CPU资源敏感,在并发阶段会占用一部分线程,导致总吞吐量下降,
  2. 无法处理浮动垃圾(在清理阶段产生的新垃圾),可能出现Concurrent Mode Failure,
  3. 使用标记-清除算法,会产生内存碎片,

适用场景:

  1. 互联网应用或B/S系统服务端,
  2. 对响应时间有要求的中大型应用,

我在一个电商项目中使用过CMS收集器,能够很好地控制GC停顿时间,保证了购物车结算过程的流畅体验,但同时也不得不配置更多的内存以应对浮动垃圾问题,

4.7 G1收集器 - 全能型收集器

G1(Garbage-First)收集器是JDK 7推出的一款面向服务端应用的垃圾收集器,被设计为旨在取代CMS收集器,在JDK 9中已成为默认垃圾收集器,
在这里插入图片描述

G1收集器有几个鲜明的特点:

  1. 并行与并发:充分利用多核CPU,缩短STW时间,
  2. 分代收集:兼顾新生代和老年代,不需要配合其他收集器使用,
  3. 空间整合:基于标记-整理算法,不会产生内存碎片,
  4. 可预测的停顿:能够指定期望的停顿时间,

与其他收集器最大的不同是,G1将堆内存划分为多个大小相等的区域(Region),不要求各个Region的连续性,每个Region都可以扮演Eden,Survivor或者老年代的角色,且角色可以随时转换,收集过程中会计算每个Region的回收价值(回收所获得的空间大小和回收所需时间的比值),优先回收价值高的Region,这也是名称"Garbage-First"的由来,

G1的工作过程相对复杂,包括初始标记(STW),并发标记,最终标记(STW),筛选回收(STW)等阶段,

优点:

  1. 并发收集,低停顿,
  2. 整体上是标记-整理算法,不会产生空间碎片,
  3. 可以设定目标停顿时间,更好地控制GC停顿对应用的影响,

缺点:

  1. 在非常大的堆和高对象分配率的场景下,可能遇到并发模式失败,
  2. 相比CMS需要更多的内存开销,

适用场景:

  1. 服务端应用,特别是需要更大堆内存的应用,
  2. 需要低延迟同时也关注吞吐量的系统,

我在一个大数据处理系统中使用G1收集器,它能够很好地控制GC停顿时间,同时有效处理大量的临时对象,是一个非常均衡的选择,

4.8 ZGC收集器 - 超低延迟收集器

ZGC(Z Garbage Collector)是JDK 11引入的一款低延迟垃圾收集器,旨在将STW时间控制在10ms以内,
在这里插入图片描述

4.9 Shenandoah收集器 - 另一种低延迟选择

Shenandoah是Red Hat开发的一款低延迟垃圾收集器,与ZGC类似,目标是降低GC停顿时间,它在JDK 12中被引入,

Shenandoah与ZGC有相似的设计目标,也使用了并发标记和并发整理算法,但实现细节不同,Shenandoah使用Brooks转发指针而非着色指针,

优点:

  1. 低延迟,大部分工作与应用线程并发执行,
  2. 停顿时间与堆大小无关,

缺点:

  1. 对吞吐量有一定影响,
  2. 内存占用略高,

适用场景:

  1. 对延迟敏感的应用,
  2. 可以接受轻微吞吐量下降的场景,

五,垃圾回收器对比与选择

了解了各种垃圾回收器的特点,如何选择适合自己应用的收集器,这里给出一个简单的对比和选择指南,
在这里插入图片描述

5.1 如何选择合适的垃圾回收器

  1. 如果应用运行在单CPU或者内存较小的环境中

    • 选择Serial + Serial Old组合,
  2. 如果是服务端应用,注重吞吐量

    • 选择Parallel Scavenge + Parallel Old组合,
  3. 如果是服务端应用,注重响应时间

    • JDK 8: 选择ParNew + CMS组合,
    • JDK 9+: 使用G1收集器,
  4. 如果是对延迟极其敏感的应用

    • JDK 11+: 使用ZGC,
    • 或者考虑Shenandoah(需要特定JDK版本支持),
  5. 如果不确定如何选择

    • JDK 8: 使用默认的Parallel收集器或者尝试CMS,
    • JDK 9+: 使用默认的G1收集器,

5.2 配置垃圾回收器

垃圾回收器的配置主要通过JVM参数来完成,以下是一些常用的配置参数:

# Serial收集器
-XX:+UseSerialGC                  # 启用Serial + Serial Old组合

# ParNew + CMS收集器
-XX:+UseConcMarkSweepGC           # 启用CMS(自动启用ParNew)
-XX:+UseCMSInitiatingOccupancyOnly # 使用设定的阈值启动CMS,而不是自适应调节
-XX:CMSInitiatingOccupancyFraction=70 # 设定CMS启动阈值为老年代使用70%时

# Parallel收集器
-XX:+UseParallelGC                # 启用Parallel Scavenge(新生代)
-XX:+UseParallelOldGC             # 启用Parallel Old(老年代)
-XX:ParallelGCThreads=4           # 设置并行收集线程数
-XX:MaxGCPauseMillis=100          # 设置最大停顿时间为100毫秒
-XX:GCTimeRatio=99                # 设置吞吐量目标(1/(1+99)=1%的时间用于GC)

# G1收集器
-XX:+UseG1GC                      # 启用G1收集器
-XX:MaxGCPauseMillis=200          # 设置最大停顿时间为200毫秒
-XX:G1HeapRegionSize=2m           # 设置Region大小为2MB
-XX:InitiatingHeapOccupancyPercent=45 # 设置触发标记周期的Java堆占用率阈值

# ZGC收集器
-XX:+UseZGC                       # 启用ZGC收集器
-XX:ZCollectionInterval=5         # 设置ZGC周期为5-XX:+UnlockExperimentalVMOptions  # 如果是JDK11-14需要添加此选项
-XX:ConcGCThreads=2               # 设置并发GC线程数

# Shenandoah收集器
-XX:+UseShenandoahGC              # 启用Shenandoah收集器
-XX:ShenandoahGCHeuristics=adaptive # 设置使用自适应策略
-XX:+UnlockExperimentalVMOptions  # 如果是JDK12-14需要添加此选项

# 通用GC参数
-Xms4g                            # 设置初始堆大小为4GB
-Xmx4g                            # 设置最大堆大小为4GB
-XX:+HeapDumpOnOutOfMemoryError   # 发生OOM时生成堆转储文件
-XX:HeapDumpPath=/path/to/dumps   # 设置堆转储文件路径
-XX:+PrintGCDetails               # 打印详细GC日志
-XX:+PrintGCDateStamps            # 打印GC时间戳
-Xloggc:/path/to/gc.log           # 设置GC日志路径(JDK8)
-Xlog:gc*:file=/path/to/gc.log    # 设置GC日志路径(JDK9+)

六,垃圾回收调优实战

在实际工作中,垃圾回收调优是一个反复试验和优化的过程,我们来看一个真实的调优案例,

6.1 案例:电商平台订单系统GC优化

问题描述

我曾经负责一个电商平台的订单系统,在促销活动期间,系统经常出现响应延迟高,甚至偶尔会出现超时问题,通过监控发现是GC停顿时间过长导致的,

初始状态

系统使用JDK 8,默认的Parallel GC,配置如下:

-Xms4g -Xmx4g -XX:+UseParallelGC

GC日志显示,每次Full GC停顿时间长达1-2秒,严重影响用户体验,

调优过程
  1. 第一步:收集数据

首先启用详细GC日志:

-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/path/to/gc.log

分析GC日志,发现老年代使用率增长迅速,每隔约10分钟就会触发一次Full GC,

  1. 第二步:更换CMS收集器

考虑到系统对响应时间敏感,更换为CMS收集器:

-Xms6g -Xmx6g -XX:+UseConcMarkSweepGC -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70

停顿时间有所改善,但仍然偶尔出现长时间停顿,分析发现是发生了"Concurrent Mode Failure",导致系统退化到使用Serial Old收集器进行Full GC,

  1. 第三步:调整内存分配

增大堆内存并优化内存分配比例:

-Xms8g -Xmx8g -XX:+UseConcMarkSweepGC -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 -XX:NewRatio=2

"Concurrent Mode Failure"的频率降低了,但还是会出现,同时发现系统中有大量短生命周期的小对象,

  1. 第四步:优化代码

分析代码,发现系统中存在大量不必要的对象创建,特别是在热点代码路径中,进行了如下优化:

  • 使用对象池复用频繁创建的对象,
  • 减少字符串拼接操作,优先使用StringBuilder,
  • 使用批量操作替代循环中的单条数据库操作,减少中间对象,

代码优化后,对象创建量显著减少,GC压力降低,

  1. 第五步:升级到G1收集器

最终,我们决定升级JDK版本并使用G1收集器:

-Xms8g -Xmx8g -XX:+UseG1GC -XX:MaxGCPauseMillis=200
优化结果

经过上述调优,系统在促销高峰期的表现得到显著改善:

  • GC停顿时间从1-2秒降低到了200ms以内,
  • 系统平均响应时间从500ms降低到了150ms,
  • 促销期间的订单处理能力提升了约40%,

6.2 垃圾回收调优的一般步骤

  1. 确定目标

    • 是减少停顿时间,还是提高吞吐量,
  2. 收集GC数据

    • 启用GC日志,
    • 使用监控工具如JVisualVM,GCViewer等分析GC行为,
  3. 选择合适的垃圾回收器

    • 根据应用特点和目标选择,
  4. 调整内存大小和比例

    • 堆大小(-Xms,-Xmx),
    • 新生代与老年代比例(-XX:NewRatio),
    • Eden与Survivor比例(-XX:SurvivorRatio),
  5. 优化代码

    • 减少不必要的对象创建,
    • 使用对象池,
    • 避免过大对象,
  6. 测试和调整

    • 在类生产环境中进行测试,
    • 根据结果持续调整参数,
  7. 持续监控

    • 建立监控系统,持续观察GC情况,
    • 随着业务变化及时调整参数,

七,常见面试题解析

作为面试官,我经常会问JVM垃圾回收相关的问题,以下是一些高频面试题及答案,

7.1 如何判断对象是否可以被回收

答:主要有两种算法:引用计数法和可达性分析算法,JVM主要使用可达性分析算法,以GC Roots为起点进行搜索,如果对象不可达,则标记为可回收对象,

GC Roots包括:虚拟机栈中引用的对象,方法区中类静态属性引用的对象,方法区中常量引用的对象,本地方法栈中JNI引用的对象,

7.2 垃圾收集算法有哪些

答:主要有三种基本算法:

  1. 标记-清除算法:标记所有需回收对象,统一回收,缺点是效率低,产生碎片,
  2. 复制算法:将内存分为两块,每次只用一块,回收时将存活对象复制到另一块,缺点是内存利用率低,
  3. 标记-整理算法:标记后将存活对象移向一端,然后清理端边界以外的内存,

实际上JVM通常采用分代收集算法,根据对象存活周期的不同将内存划分为几块,新生代使用复制算法,老年代使用标记-整理或标记-清除算法,

7.3 描述下CMS收集器的工作原理

答:CMS(Concurrent Mark Sweep)是一种以获取最短回收停顿时间为目标的收集器,工作分为四个步骤:

  1. 初始标记:STW,仅标记GC Roots能直接关联的对象,
  2. 并发标记:耗时最长,并发进行,不会停止用户线程,
  3. 重新标记:STW,修正并发标记期间因用户程序继续运行而导致的标记变动,
  4. 并发清除:并发进行,不会停止用户线程,

优点是并发收集,低停顿,缺点是对CPU资源敏感,无法处理浮动垃圾,会产生空间碎片,

7.4 G1收集器的优势在哪里

答:G1 (Garbage-First) 收集器的优势:

  1. 并发与并行:利用多核CPU,使用并行和并发来减少停顿,
  2. 分代收集:仍然保留分代概念,但物理上不要求内存连续,
  3. 空间整合:基于标记-整理算法,不会产生内存碎片,
  4. 可预测的停顿:能够设定目标停顿时间(-XX:MaxGCPauseMillis),
  5. Region化内存布局:将堆分成相同大小的区域(Region),回收时可以有选择地回收部分Region,

7.5 ZGC相比于G1有什么改进

答:ZGC相比G1的主要改进:

  1. 更低的延迟:ZGC的STW时间通常在10ms以内,远低于G1,
  2. 更大的堆支持:ZGC可以高效处理TB级别的堆,
  3. 着色指针:ZGC使用64位指针的一些位作为标记位,实现快速对象定位,
  4. 读屏障:保证线程安全的同时最小化停顿时间,
  5. 停顿时间不随堆或存活对象数量增加而增加,

不过ZGC目前仅支持JDK 11及以上版本,且主要在Linux/x64平台上工作最佳,

7.6 Major GC和Full GC的区别

答:

  • Minor GC:清理新生代,频率较高,时间较短,
  • Major GC:清理老年代,通常伴随至少一次Minor GC,
  • Full GC:清理整个堆内存,包括新生代,老年代和永久代(JDK 8前)或元空间(JDK 8+),

在CMS等垃圾收集器的上下文中,Major GC和Full GC有时会被混用,但严格来说它们是不同的概念,

7.7 什么情况下会触发Full GC

答:触发Full GC的情况:

  1. 老年代空间不足,
  2. 永久代/元空间空间不足,
  3. 手动调用System.gc()(但实际是否执行取决于JVM实现),
  4. CMS GC时出现promotion failed和concurrent mode failure,
  5. 统计信息数据决定需要进行一次Full GC,
  6. 堆中分配很大的对象,Eden区和两个Survivor区都放不下,

八,总结与展望

JVM垃圾回收技术经历了从单线程到多线程,从串行到并行再到并发,从全堆扫描到分代收集再到区域化收集的演变过程,目标始终是在保证系统稳定运行的前提下,尽可能减少垃圾回收对应用性能的影响,

现在的JVM垃圾回收器已经相当成熟,从Serial,ParNew,Parallel,CMS,到G1,ZGC,Shenandoah,每一代收集器都有其特点和适用场景,我们需要根据应用特性选择合适的收集器,

未来垃圾回收技术的发展方向主要有:

  1. 进一步减少停顿时间,甚至实现零停顿,
  2. 自适应学习的智能收集策略,
  3. 更好的硬件利用,如多核CPU,NUMA架构,非易失性内存等,
  4. 大数据,云原生时代的内存管理新挑战,

作为Java开发者,我们不需要了解垃圾回收的所有细节,但理解基本原理和各收集器的特点,可以作为Java开发者,可以帮助我们写出更高效的代码,也能在遇到内存问题时更快地定位和解决,

在实际工作中,我发现很多性能问题并非JVM本身的限制,而是由于代码层面的不合理设计导致的,比如创建过多临时对象,内存泄漏,或者对象过大等,因此,良好的编码习惯与合理的GC配置同样重要。

记住,最好的垃圾回收是不需要垃圾回收,编写高效代码,减少不必要的对象创建,才是解决问题的根本之道,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值