Java 的垃圾收集(GC)机制是 JVM 提供的自动内存管理功能,帮助开发者避免手动内存管理的问题。在本文中,我们将通过代码示例,深入探讨 Java 垃圾收集的工作原理,包括 对象存活判定垃圾回收流程内存策略等内容。

目录

  1. Java 垃圾收集的基本概念
  2. 对象存活判定:如何判断对象是否可以被回收
  3. 垃圾回收流程:从标记到清除
  4. 内存管理策略:JVM 堆与非堆的划分
  5. 常见的垃圾收集器与算法
  6. 垃圾收集优化与调优
  7. 总结

1. Java 垃圾收集的基本概念

Java 中的垃圾收集机制可以自动管理内存,避免了内存泄漏和溢出等问题。GC 是 JVM 中的核心功能之一,它通过自动回收不再使用的对象来释放内存空间。垃圾收集器依据对象的 可达性分析 来判定哪些对象可以回收。

在 Java 中,GC 会通过 可达性分析 作为主要算法判断对象是否存活,若对象不可达,则会被标记为垃圾,准备回收。


2. 对象存活判定:如何判断对象是否可以被回收

2.1 GC Roots

GC Roots 是垃圾回收的根基,它包括一些不受垃圾收集器控制的对象,如虚拟机栈中的引用、常量池中的引用等。

存活对象判定 依据这些 GC Roots 来判定对象是否存活。若一个对象没有直接或间接地通过 GC Roots 引用,则被认为是不可达的对象,可以被回收。

示例代码:

class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println(this.name + " is being collected");
    }
}

public class GCExample {
    public static void main(String[] args) {
        Person person1 = new Person("John");
        Person person2 = new Person("Alice");

        person1 = null; // Person1 becomes eligible for GC

        System.gc(); // Request JVM to perform garbage collection
    }
}

2.2 说明

  • 代码中创建了两个 Person 对象,并且通过将 person1 置为 null,使其变为垃圾对象。
  • 通过调用 System.gc() 来手动触发垃圾回收。注意,JVM 在实际运行时不一定立即进行垃圾回收,手动触发的垃圾回收可能会被忽略。

输出(可能):

John is being collected

2.3 可达性分析

通过以上代码,person1 变为不可达对象,JVM 会将其标记为垃圾对象,然后进行回收。


3. 垃圾回收流程:从标记到清除

垃圾回收的过程可以简化为以下步骤:

3.1 标记(Marking)

JVM 会通过可达性分析算法从 GC Roots 开始,遍历所有可达对象,并将这些对象标记为“存活”。

3.2 清除(Clearing)

在标记过程之后,JVM 会回收所有没有被标记为存活的对象,并释放它们占用的内存。

3.3 整理(Compaction)

垃圾回收后,堆内存中可能会存在空洞或碎片,因此 JVM 会对存活对象进行整理,将它们挪到堆的一侧,回收不再使用的内存空间。


4. 内存管理策略:JVM 堆与非堆的划分

JVM 内存管理分为 堆(Heap)非堆(Non-Heap)。垃圾收集器主要关注堆内存。

4.1 堆内存

  • 年轻代(Young Generation):新创建的对象首先分配在年轻代,年轻代又分为 Eden 区 和两个 Survivor 区(S0 和 S1)。
  • 老年代(Old Generation):存活时间较长的对象会被移入老年代。
  • 永久代(Permanent Generation):存储类的元数据、常量池等(JDK 8 后,永久代被替换为 Metaspace)。

4.2 非堆内存

包括方法区和直接内存(Direct Memory)。方法区用于存放类的信息,如类的字节码、静态变量等。


5. 常见的垃圾收集器与算法

5.1 Serial 收集器

Serial 收集器是一个单线程收集器,它通过一个线程来执行所有的垃圾回收操作。适用于内存比较小或者对垃圾回收暂停时间要求不高的环境。

5.2 Parallel 收集器

Parallel 收集器使用多个线程来进行垃圾回收,适用于多核机器,能够提高吞吐量。

5.3 CMS(Concurrent Mark-Sweep)收集器

CMS 旨在尽量减少垃圾回收时的停顿时间,适合对低延迟有较高需求的应用。

5.4 G1 收集器

G1 收集器是从 JDK7 起出现的,旨在提供可预测的停顿时间,适合大内存、高吞吐量和低延迟的应用。


6. 垃圾收集优化与调优

6.1 GC 日志分析

通过 JVM 参数 配置 GC 日志,分析垃圾回收的行为,可以帮助优化 GC 过程。

java -Xlog:gc* -jar MyApplication.jar

6.2 调优策略

  • 调整堆大小:通过 -Xms-Xmx 参数调整堆的初始和最大大小。
  • 选择合适的收集器:根据应用需求选择不同的垃圾收集器(如 -XX:+UseG1GC 启用 G1 收集器)。
  • 避免频繁创建短生命周期对象:减少无意义的对象创建,可以减少垃圾回收的负担。

7. 总结

Java 的垃圾收集机制是通过自动管理内存来减轻开发者的负担,它包括 可达性分析垃圾回收的标记-清除-整理流程JVM 堆与非堆的划分 等概念。JVM 提供了多种垃圾收集器,每个收集器都有不同的特点,适用于不同的场景。

  • 标记-清除-整理 是垃圾回收的基础过程。
  • 可达性分析 是判断对象存活与否的关键算法。
  • 内存管理策略垃圾收集器的选择 是优化性能的关键。

理解垃圾收集的原理和调优策略能够帮助开发者在高并发、内存紧张等场景下编写更加高效和稳定的 Java 程序。