Java 中的垃圾回收机制如何工作?

17 Mar 2025 | 6 分钟阅读

Java 中,垃圾回收是一种自动管理内存的过程。它会查找未使用的对象(即程序不再使用的对象)并将其删除或移除,以释放内存。垃圾回收机制使用了多种 GC 算法。其中最常用、最流行的算法是标记-清除

在本节中,我们将学习对象何时符合垃圾回收的条件、垃圾回收的类型以及标记-清除算法。此外,我们还将理解Java 中的垃圾回收机制是如何工作的?

垃圾回收器概述

当程序在 Java 中执行时,它会以不同的方式使用内存。堆(heap)是对象存在的内存区域。它是唯一参与垃圾回收过程的内存部分。它也被称为可回收堆。所有垃圾回收都确保堆尽可能地拥有空闲空间。垃圾回收器的功能是查找并删除无法访问的对象。

关于垃圾回收器要点

  • 它由一个称为垃圾回收器的线程控制。
  • Java 提供了两个方法 System.gc()Runtime.gc(),它们向 JVM 发送垃圾回收请求。请记住,垃圾回收不一定会发生。
  • Java 程序员无需进行内存管理。我们无法强制垃圾回收器进行垃圾回收,这取决于 JVM。
  • 如果堆内存已满,JVM 将不允许创建新对象,并会显示错误 java.lang.OutOfMemoryError
  • 当垃圾回收器从内存中移除对象时,首先,垃圾回收器线程会调用该对象的 finalize() 方法,然后进行移除。

对象分配

当对象被分配时,JRockit JVM 会检查对象的大小。它区分小对象和大对象。大小的区分取决于 JVM 的版本、堆大小、垃圾回收策略以及使用的平台。对象的大小通常在 2 到 128 KB 之间。

小对象存储在线程本地区域(TLA),这是堆中的一个空闲块。TLA 不与其他线程同步。当 TLA 变满时,它会请求新的 TLA。

另一方面,不适合直接放入 TLA 的大对象会直接分配到堆中。如果一个线程正在使用年轻代,那么它会直接存储在老年代。大对象需要线程之间更多的同步。

对象何时符合垃圾回收条件?

如果一个对象不再被任何程序、线程、静态引用或其引用为 null 所使用,那么它就符合垃圾回收的条件。如果两个对象相互引用(循环引用)但没有任何活动的引用,那么这两个对象都会被垃圾回收器回收。在其他一些情况下,对象也符合垃圾回收的条件:

  • 如果该对象的引用被显式设置为 null
  • 如果对象在某个代码块内创建,并且一旦控制流退出该代码块,该引用就超出作用域,那么该对象也符合回收条件。

Java 垃圾回收器做什么?

JVM 控制垃圾回收器。JVM 决定何时执行垃圾回收。你也可以请求 JVM 运行垃圾回收器。但在任何情况下,都不能保证 JVM 会遵守。当 JVM 检测到内存不足时,它会运行垃圾回收器。当 Java 程序请求垃圾回收器运行时,JVM 通常会很快地响应请求。它不保证请求一定会被接受。

需要理解的关键点是“对象何时符合垃圾回收条件?”

每个 Java 程序都有多个线程。每个线程都有其执行栈。在 Java 程序中运行的一个线程是 main() 方法。现在我们可以说,当没有活动的线程能够访问一个对象时,该对象就符合垃圾回收条件。垃圾回收器将该对象视为可删除的对象。如果一个程序有一个引用变量指向一个对象,并且该引用变量可以被活动的线程访问,那么这个对象被称为可达对象

这时会产生一个问题:“Java 应用程序会耗尽内存吗?”

答案是肯定的。垃圾回收系统会尝试在对象不再使用时将其从内存中移除。尽管如此,如果你维护了大量活动的(live)对象,垃圾回收并不能保证有足够的内存。只有可用的内存会被有效地管理。

垃圾回收的类型

有五种类型的垃圾回收,如下所示:

  • 串行 GC(Serial GC):它对年轻代和老年代使用标记-清除方法,这分别是 minor GC 和 major GC。
  • 并行 GC(Parallel GC):它与串行 GC 类似,但它会为年轻代垃圾回收生成 N(系统中 CPU 核心数)个线程。
  • 并行旧 GC(Parallel Old GC):它与并行 GC 类似,但它对两个代(年轻代和老年代)都使用多线程。
  • 并发标记-清除 (CMS) 收集器:它对老年代进行垃圾回收。你可以使用 XX:ParalleCMSThreads=JVM 选项限制 CMS 收集器中的线程数。它也被称为并发低暂停收集器。
  • G1 垃圾回收器:它在 Java 7 中引入。其目标是取代 CMS 收集器。它是一个并行、并发且与 CMS 收集器不同的收集器。它没有年轻代和老年代的概念。它将堆划分为多个大小相等的区域。它首先回收包含较少活动数据的区域。

标记和清除算法

JRockit JVM 使用标记-清除算法进行垃圾回收。它包含两个阶段:标记阶段和清除阶段。

标记阶段:从线程、本地句柄和其他 GC 根源可访问的对象被标记为活动的。每个对象树都有一个或多个根对象。GC 根始终是可达的。所以,任何具有垃圾回收根的对象都与根相关联。它识别并标记所有正在使用的对象,其余的可以被认为是垃圾。

How Garbage Collection Works in Java

清除阶段:在此阶段,遍历堆以查找活动对象之间的空隙。这些空隙被记录在空闲列表中,可用于新对象的分配。

标记-清除算法有两种改进版本:

  • 并发标记-清除
  • 并行标记-清除

并发标记-清除

它允许线程在垃圾回收的大部分过程中继续运行。有以下几种标记方式:

  • 初始标记:它识别活动对象的根集。这在线程暂停时完成。
  • 并发标记:在此标记过程中,会跟踪根集的引用。它在堆中查找并标记剩余的活动对象。这在线程运行时完成。
  • 预清理标记:它识别并发标记所做的更改。找到并标记其他活动对象。这在线程运行时完成。
  • 最终标记:它识别预清理标记所做的更改。找到并标记其他活动对象。这在线程暂停时完成。

并行标记-清除

它利用系统中所有可用的 CPU 来尽可能快地执行垃圾回收。它也被称为并行垃圾回收器。当并行垃圾回收执行时,线程不会执行。

标记-清除的优点

  • 这是一个周期性过程。
  • 这是一个无限循环。
  • 算法执行期间不允许额外的开销。

标记-清除的缺点

  • 在垃圾回收算法运行时,会停止正常的程序执行。
  • 它会在程序上运行多次。

下一主题打开对话框