垃圾收集机制(在什么时候,对什么,做了什么)

Java 垃圾收集机制:时机、对象与操作全解析

Java 垃圾收集(Garbage Collection,GC)是 JVM 自动管理内存的核心机制,旨在回收不再使用的对象所占用的内存,避免内存泄漏和溢出。以下从触发时机、回收对象、执行操作三个核心维度详细解析,并结合示例代码展示关键场景:

一、垃圾收集的触发时机

GC 并非实时执行,而是在特定条件下触发,分为 Minor GC(新生代 GC)、Major GC(老年代 GC)和 Full GC(整堆 GC)三类:

1. Minor GC(新生代 GC)

  • 触发时机:当新生代的 Eden 区满,或新对象分配内存时 Eden 区空间不足。
  • 触发频率:频繁(新生代对象生命周期短,回收频繁)。
  • 触发原因:新生代采用 "复制算法",Eden 区和 Survivor 区空间有限,对象快速填满后需回收。

2. Major GC(老年代 GC)

  • 触发时机:老年代空间不足时触发(如新生代对象晋升到老年代,老年代剩余空间不足)。
  • 触发频率:较低(老年代对象生命周期长,回收成本高)。
  • 关联操作:Major GC 通常伴随一次 Minor GC("晋升担保" 机制:若 Minor GC 后仍有大量对象需晋升,会触发 Major GC)。

3. Full GC(整堆 GC)

  • 触发时机
    • 老年代空间不足且 Major GC 后仍无法释放足够内存;
    • 方法区(元空间)空间不足;
    • 调用System.gc()(仅建议 JVM 执行 GC,不保证立即触发);
    • CMS GC 出现 "Concurrent Mode Failure"(并发回收时老年代空间不足)。
  • 特点:回收整个堆(新生代 + 老年代 + 元空间),暂停时间长,性能影响大。

二、垃圾收集的目标对象:"无用对象"

GC 回收的是堆内存中不再被引用的对象,JVM 通过以下机制判断对象是否 "无用":

1. 引用计数法(早期算法,存在缺陷)

  • 原理:为每个对象维护一个引用计数器,被引用时 + 1,引用失效时 - 1;计数器为 0 则视为无用对象。
  • 缺陷:无法解决循环引用(如 A 引用 B,B 引用 A,计数器均不为 0,但实际无外部引用),因此现代 JVM 已不再使用。

2. 可达性分析算法(主流算法)

  • 原理:以 "GC Roots" 为起点,向下遍历对象引用链,若对象不可达(不在任何引用链上),则视为无用对象。
  • GC Roots 包含
    • 虚拟机栈(栈帧中的局部变量表)中引用的对象;
    • 方法区中类静态属性引用的对象;
    • 方法区中常量引用的对象;
    • 本地方法栈中 JNI(Native 方法)引用的对象;
    • JVM 内部的引用(如基本数据类型对应的 Class 对象、异常对象等)。

3. 引用类型与回收策略

Java 中的引用分为 4 类,不同引用类型的对象回收时机不同:

  • 强引用Object obj = new Object()):GC 绝不会回收,即使 OOM 也不回收;
  • 软引用SoftReference):内存不足时回收,用于缓存;
  • 弱引用WeakReference):GC 时立即回收,用于临时对象;
  • 虚引用PhantomReference):仅用于跟踪对象回收,必须配合引用队列使用。

三、垃圾收集的核心操作:回收与内存整理

GC 的核心操作包括标记无用对象、回收内存和整理内存碎片,不同垃圾收集器采用不同算法实现:

1. 标记阶段:识别无用对象

通过可达性分析算法标记所有可达对象,未被标记的即为无用对象(待回收)。

2. 回收阶段:释放无用对象内存

根据不同算法,回收方式分为:

  • 复制算法(新生代)

    • 将新生代分为 Eden 区和两个 Survivor 区(From、To);
    • 回收时将 Eden 和 From 区的存活对象复制到 To 区,清空 Eden 和 From 区;
    • 交换 From 和 To 区角色,重复此过程;
    • 优点:无内存碎片;缺点:浪费部分内存(To 区闲置)。
  • 标记 - 清除算法(老年代)

    • 标记无用对象后直接清除;
    • 优点:无需复制对象;缺点:产生内存碎片,影响后续内存分配。
  • 标记 - 整理算法(老年代)

    • 标记无用对象后,将存活对象向内存一端移动,再清除边界外的内存;
    • 优点:无内存碎片;缺点:需移动对象,成本较高。

3. 内存整理:优化内存布局

  • 复制算法天然无碎片;标记 - 整理算法通过移动对象消除碎片;
  • 部分收集器(如 CMS)采用 "标记 - 清除",会产生碎片,需通过 Full GC 进行整理。

4. 元空间(方法区)回收

  • 回收内容 :无用的类(满足:该类所有实例已回收、类加载器已回收、Class对象无引用)、常量池中的无用常量;
  • 触发时机:元空间内存不足时。

四、垃圾收集器的执行特点

  • 自动性 :JVM 自动触发,无需程序员手动回收(不同于 C++ 的delete);
  • STW(Stop-The-World):多数 GC 操作会暂停所有用户线程(Minor GC 暂停时间短,Full GC 暂停时间长);
  • 分代收集:基于对象生命周期将堆分为新生代和老年代,采用不同收集策略(新生代用复制算法,老年代用标记 - 清除 / 整理算法);
  • 并发收集:部分收集器(如 CMS、G1)支持并发回收,减少 STW 时间。

五、示例代码

1. 基础示例:对象成为垃圾的场景

java 复制代码
public class GcBasicExample {
    public static void main(String[] args) {
        // 创建对象并赋值给强引用
        Object obj1 = new Object();
        Object obj2 = obj1; // obj2也引用该对象

        System.out.println("对象引用状态:obj1=" + obj1 + ", obj2=" + obj2);

        // 断开所有引用,对象变为垃圾
        obj1 = null;
        obj2 = null;

        // 建议JVM执行GC(仅建议,不保证立即执行)
        System.gc();
        System.out.println("已触发GC建议,原对象已无引用");
    }
}

2. 引用类型对 GC 的影响示例

(1)软引用(内存不足时回收)
java 复制代码
import java.lang.ref.SoftReference;

public class SoftReferenceExample {
    public static void main(String[] args) {
        // 创建软引用指向对象
        SoftReference<Object> softRef = new SoftReference<>(new Object() {
            @Override
            protected void finalize() throws Throwable {
                super.finalize();
                System.out.println("软引用对象被GC回收");
            }
        });

        System.out.println("GC前软引用获取对象:" + softRef.get());

        // 模拟内存不足(需配置JVM参数:-Xmx20M)
        try {
            byte[] bigArray = new byte[15 * 1024 * 1024]; // 占用15MB内存
        } catch (OutOfMemoryError e) {
            e.printStackTrace();
        }

        System.out.println("内存不足后软引用获取对象:" + softRef.get());
    }
}
(2)弱引用(GC 时立即回收)
java 复制代码
import java.lang.ref.WeakReference;

public class WeakReferenceExample {
    public static void main(String[] args) {
        WeakReference<Object> weakRef = new WeakReference<>(new Object() {
            @Override
            protected void finalize() throws Throwable {
                super.finalize();
                System.out.println("弱引用对象被GC回收");
            }
        });

        System.out.println("GC前弱引用获取对象:" + weakRef.get());

        // 触发GC
        System.gc();

        // 暂停线程,确保GC完成
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("GC后弱引用获取对象:" + weakRef.get());
    }
}

3. Finalize 方法:对象回收前的操作

java 复制代码
public class FinalizeExample {
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("FinalizeExample对象即将被GC回收");
    }

    public static void main(String[] args) {
        FinalizeExample obj = new FinalizeExample();
        System.out.println("创建对象:" + obj);
        
        // 断开引用
        obj = null;
        
        // 触发GC
        System.gc();
        
        // 等待GC执行
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

4. 观察 Minor GC 触发(打印 GC 日志)

java 复制代码
import java.util.ArrayList;
import java.util.List;

public class GcLogExample {
    public static void main(String[] args) {
        List<byte[]> list = new ArrayList<>();
        
        // 循环创建对象,填满新生代Eden区触发Minor GC
        for (int i = 0; i < 100; i++) {
            list.add(new byte[1024 * 1024]); // 每次创建1MB对象
            System.out.println("已创建第" + (i+1) + "个对象");
        }
    }
}

运行配置 :需添加 JVM 参数打印 GC 日志-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:+PrintGCTimeStamps(参数说明:堆内存 20MB,新生代 10MB,打印详细 GC 日志)

六、总结

维度 具体内容
什么时候 新生代 Eden 区满触发 Minor GC;老年代不足触发 Major GC;整堆不足触发 Full GC
对什么 堆中不可达的对象(通过可达性分析判断)、方法区的无用类和常量
做了什么 标记无用对象→通过复制 / 标记 - 清除 / 标记 - 整理算法回收内存→整理内存碎片(可选)

垃圾收集机制的核心目标是自动释放无用内存,平衡回收效率与应用性能,不同垃圾收集器(如 Serial、Parallel、CMS、G1、ZGC)通过优化算法减少 STW 时间,适应不同应用场景的需求。示例代码直观展示了对象成为垃圾的条件、不同引用类型的回收特性及 GC 触发的实际表现。

相关推荐
张人大 Renda Zhang16 小时前
Java 虚拟线程 Virtual Thread:让“每请求一线程”在高并发时代复活
java·jvm·后端·spring·架构·web·虚拟线程
杀死那个蝈坦18 小时前
Caffeine
java·jvm·spring cloud·tomcat
i***132420 小时前
java进阶1——JVM
java·开发语言·jvm
喜欢流萤吖~1 天前
JVM垃圾回收机制
jvm
平原人1 天前
JVM字节码常量池解析
jvm·常量池·字节码
nono牛1 天前
C++ 语言全面教程 (基础入门)
java·jvm·c++
Zzzzzxl_1 天前
深入理解Java JVM中的垃圾回收器
java·jvm·编程·性能调优·垃圾回收
〝七夜5691 天前
JVM内存结构
java·开发语言·jvm
一只小透明啊啊啊啊2 天前
垃圾回收算法有哪些
java·jvm