在Java中什么是垃圾回收?Java GC基本原理解析

来源:安卓教程作者:关中王头衔:草根站长
导读:本期聚焦于关中王创作的《在Java中什么是垃圾回收?Java GC基本原理解析》,敬请观看详情。垃圾回收是Java自动内存管理的核心机制,它负责识别并清理堆内存中不再被使用的对象,从而把开发者从手动释放内存的负担中解放出来。本文将从JVM内存区域划分讲起,深入分析可达性分析算法、引用计数法的区别,介绍常见的垃圾收集器如Serial、Parallel、CMS、G1的工作特点和适用场景,并分享GC调优的实用思路,帮助你理解垃圾回收的底层运行逻辑,写出更高效稳定的Java程序。

垃圾回收(Garbage Collection,简称GC)是Java语言最重要的特性之一。在没有GC的语言中,比如C或C++,开发者必须手动调用free或delete来释放内存,一旦忘记释放就会产生内存泄漏,一旦重复释放就会导致程序崩溃。Java通过虚拟机自动管理内存,把判定对象是否存活、回收无用内存这些工作交给了垃圾回收器,开发者只需要关注业务逻辑本身。不过GC并不是万能的,理解它的基本原理,对于排查内存泄漏、优化程序性能至关重要。

在Java中什么是垃圾回收?Java GC基本原理解析

一、GC回收的区域在哪里:先弄懂JVM内存划分

要理解垃圾回收,首先要明白GC回收的是哪块内存。JVM运行时数据区主要分为堆、虚拟机栈、本地方法栈、方法区和程序计数器几个部分。其中虚拟机栈、本地方法栈和程序计数器是线程私有的,随线程生随线程灭,不需要垃圾回收器操心。真正需要GC介入的是堆和方法区(在JDK 8之后由元空间实现)。

堆是对象实例的主要存放地,也是GC工作的主战场。为了提高回收效率,堆通常被划分为新生代和老年代两块。新生代又分为一个Eden区和两个Survivor区,默认比例是8:1:1。绝大多数新创建的对象首先在Eden区分配,经过一次Minor GC后存活的对象会被移动到Survivor区,每熬过一次GC年龄就加一,当年龄达到阈值(默认15)时晋升到老年代。这种分代设计基于一个经验规律:大多数对象的生命周期都很短,朝生夕死,针对不同年龄段的对象采用不同的回收策略能显著提升效率。

老年代存放的是长期存活的大对象和从新生代晋升过来的对象。当老年代空间不足时会触发Major GC或Full GC,这类回收通常涉及整个堆和方法区,耗时长、停顿明显,是性能调优时重点关注的对象。

二、如何判断对象已经死亡:两种判定算法

垃圾回收器要回收内存,第一步就是判断哪些对象是无用的。目前主要有两种判定思路。

第一种是引用计数算法,做法是给每个对象维护一个引用计数器,被引用一次加一,引用失效减一,计数为零就判定可回收。这种方法实现简单、判定效率高,但存在一个致命缺陷:无法处理循环引用。假设对象A引用对象B,对象B又引用对象A,即使它们都不再被外界使用,各自的计数器都不为零,永远不会被回收。Python语言采用的是这种算法并配合额外手段解决循环引用,而Java虚拟机并没有选择它。

第二种是可达性分析算法,这是HotSpot虚拟机采用的主流方案。基本思路是从一系列被称为GC Roots的根对象出发,沿着引用链向下搜索,能到达的对象就是存活的,不可达的对象则判定为可回收。可作为GC Roots的对象包括:虚拟机栈中引用的对象、方法区中类的静态变量引用的对象、本地方法栈中JNI引用的对象,以及被同步锁持有的对象等。举个直观的例子,把对象之间的引用关系想象成一张图,GC Roots就是这张图的入口,从入口无法走到的节点就是孤立的无用对象,即使这些节点之间互相引用也无济于事。

public class GCDemo {
    public static void main(String[] args) {
        Object a = new Object(); // a作为局部变量,被虚拟机栈引用,是GC Roots可达对象
        Object b = new Object();
        a = null; // 原对象失去引用,下次GC时会被回收
        b = null;
        System.gc(); // 建议虚拟机执行垃圾回收,但不保证立即执行
    }
}

除了判定算法,Java还提供了四种引用强度不同的类型:强引用、软引用、弱引用和虚引用。强引用只要存在就永远不会被回收;软引用在内存不足时才会被回收,常用于缓存;弱引用只要发生GC就会被回收;虚引用则纯粹为了跟踪对象被回收的过程。灵活运用这些引用类型,可以在缓存、内存敏感场景下实现精细化的内存管理。

三、主流垃圾收集器对比:从Serial到G1

确定了要回收的对象,接下来就是怎么回收的问题。不同的垃圾收集器在回收算法和停顿时间上各有取舍,理解它们的特点才能做出合适的选择。

Serial收集器是最古老的单线程收集器,进行垃圾回收时会暂停所有用户线程(Stop The World),但它简单高效,没有线程交互开销,适合单核CPU或客户端模式下的应用。Parallel Scavenge收集器(并行收集器)是Serial的多线程版本,JDK 8的默认收集器,注重吞吐量,适合后台运算型任务,停顿时间相对较长但整体吞吐量高。

CMS(Concurrent Mark Sweep)收集器以最短停顿时间为目标,它的标记过程与用户线程并发执行,分为初始标记、并发标记、重新标记和并发清除四个阶段,其中只有初始标记和重新标记会短暂停顿。CMS的代价是消耗CPU资源、无法处理浮动垃圾,并且基于标记清除算法会产生内存碎片。需要注意的是,CMS在JDK 9之后被标记为废弃,JDK 14中已被正式移除。

G1(Garbage First)收集器是JDK 9之后的默认收集器,它把堆划分为多个大小相等的Region,不再物理隔离新生代和老年代,回收时优先处理垃圾最多的区域(这正是Garbage First名字的由来),从而在可控的停顿时间内获得最高的回收效率。G1兼顾了吞吐量和停顿时间,适合大内存、多核服务器的应用场景。JDK 11之后还出现了实验性的ZGC和Shenandoah,能够把停顿时间控制在毫秒甚至亚毫秒级别。下面的表格对常见收集器做了简单对比:

收集器线程模型算法特点适用场景
Serial单线程复制算法,停顿明显客户端小应用
Parallel Scavenge多线程并行复制算法,注重吞吐量后台计算任务
CMS并发标记清除,低停顿对响应时间敏感(已废弃)
G1并发加并行标记整理,Region分区大堆服务器应用

四、GC调优的实用思路

理解原理之后,实际开发中遇到GC频繁、停顿过长等问题时,可以从几个方面入手。首先学会分析GC日志,通过JVM参数-XX:+PrintGCDetails(JDK 9之后推荐使用-Xlog:gc*)可以输出详细的回收信息,观察Minor GC和Full GC的频率与耗时是判断问题的第一步。如果Full GC频繁发生,通常要检查是否存在内存泄漏、大对象过早进入老年代,或者堆空间设置不合理。

其次合理设置堆大小。新生代太小会导致对象过早晋升到老年代,引发频繁的Full GC;太大则会增加单次Minor GC的时间。一般建议通过压测找到平衡点,而不是盲目调大内存。最后,警惕代码层面的内存泄漏,比如静态集合持有对象引用不释放、未关闭的资源、不当的ThreadLocal使用等,这些才是GC无能为力的根源。垃圾回收再智能,也只能回收不可达的对象,被引用着的垃圾它永远无法清理。

Java垃圾回收GC原理JVM内存管理修改时间:2026-09-11 22:46:42

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0911/54949.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。