Java 虚拟机 - 垃圾收集


Java 对象的生命周期由 JVM 管理。一旦程序员创建了一个对象,我们就不需要担心它的其余生命周期。JVM会自动找到那些不再使用的对象并从堆中回收它们的内存。

垃圾收集是 JVM 执行的一项主要操作,根据我们的需求对其进行调整可以为我们的应用程序带来巨大的性能提升。现代 JVM 提供了多种垃圾收集算法。我们需要了解应用程序的需求来决定使用哪种算法。

您无法在 Java 中以编程方式释放对象,就像在 C 和 C++ 等非 GC 语言中那样。因此,Java 中不能有悬空引用。但是,您可能有空引用(引用 JVM 永远不会存储对象的内存区域)。每当使用空引用时,JVM 都会抛出 NullPointerException。

请注意,虽然由于 GC 而很少在 Java 程序中发现内存泄漏,但它们确实会发生。我们将在本章末尾创建内存泄漏。

现代 JVM 使用以下 GC

  • 串行收集器
  • 吞吐量收集器
  • CMS收集器
  • G1收集器

上述每种算法都执行相同的任务 - 查找不再使用的对象并回收它们在堆中占用的内存。一种简单的方法是计算每个对象拥有的引用数量,并在引用数量变为 0 时立即释放它(这也称为引用计数)。为什么这很天真?考虑一个循环链表。它的每个节点都会有一个对其的引用,但整个对象不会从任何地方被引用,并且应该被释放,理想情况下。

JVM 不仅释放内存,还将小内存块合并成更大的内存块。这样做是为了防止内存碎片。

简单来说,典型的 GC 算法执行以下活动 -

  • 寻找未使用的物品
  • 释放它们在堆中占用的内存
  • 合并碎片

GC 必须在运行时停止应用程序线程。这是因为它在运行时移动对象,因此无法使用这些对象。此类停止称为“stop-the-world 暂停”,我们在调整 GC 时的目标是最大限度地减少这些暂停的频率和持续时间。

内存合并

内存合并的简单演示如下所示

内存合并

阴影部分是需要释放的对象。即使回收了所有空间后,我们也只能分配最大大小 = 75Kb 的对象。即使我们有 200Kb 的可用空间,如下所示

阴影部分