Blog chevron_right Java

我为什么应该关注 Java 垃圾回收?

Why Should I Care About Java Garbage Collection?

垃圾回收在内存分配方面比其他标准库函数更快,而回收死亡对象无需任何成本。GC 无需开发人员协助即可找到所有死亡对象。但大多数回收器都会引发与 GC 相关的暂停,并且暂停时间与其堆内存大小成比例。

在本文中,您将了解:

  • 不同类型的垃圾回收器
  • Azul Platform Prime C4 回收器的独特之处
  • 关于垃圾回收调优的更多内容

在应用程序执行期间,如果对象不会再被使用,JVM 将释放未使用的内存。这个过程称为垃圾回收 (GC)。

垃圾回收远比您想象的更有用、更高效。它在内存分配方面比其他标准库函数更快,而回收死亡对象无需任何成本。GC 无需开发人员协助即可找到所有死亡对象。但在某些方面,垃圾回收也具有不易察觉的问题。大多数回收器都会引发与 GC 相关的暂停,并且暂停时间与其堆内存大小成比例。

______________________________________________________________________________ 

1 GB 的存活对象 = 1 秒暂停 

______________________________________________________________________________ 

换句话说,堆内存越大,暂停时间越长。如果您运行一个 20 分钟的测试并不断调优,直到所有暂停都消失,那么恭喜您,您只是将暂停延迟到了第 21 分钟。暂停仍然会发生,您的应用程序仍然会受到影响。而且垃圾回收甚至无法消除对象泄漏,开发人员仍然必须找出并修复持有这些泄漏对象的引用。

但让我们回过头来看看好消息。Java 确实提供了一定程度的 GC 控制能力。开发人员和架构师可以根据垃圾回收器的行为作出决策,以调整应用程序的性能。

试图在应用程序编程层面解决垃圾回收问题是危险的。要做好这件事需要大量实践和理解,而这些时间本可以更好地用于构建增值功能。即使您做出了所有正确的决策,应用程序所依赖的其他代码也很可能并未经过优化。随着应用程序工作负载随时间变化,您的应用程序仍然会遇到 GC 相关的性能问题。

此外,根据应用程序的特性,选择错误的垃圾回收器类型或使用不当的设置,可能会大幅增加暂停时间,甚至导致内存耗尽崩溃。通过正确理解垃圾回收及可用选项,您可以做出更加明智的决策,从而在运行时提升应用程序的性能和可靠性。

所有 Java 垃圾回收器都是一样的吗?

垃圾回收器分为多种类型。每种回收器或多或少具备并发性,即它可能在应用程序暂停时运行(非并发)、与应用程序的执行存在重叠(部分或基本并发),或在应用程序运行时运行(并发)。

  • 并发回收器 在应用程序继续执行的同时并发执行垃圾回收。
  • 并行回收器 使用多个线程。回收器可以是并发不并行的,也可以既是并发又是并行的。(附注:查阅较旧的垃圾回收文献时需谨慎,因为我们过去称为“并行”的,现在称为“并发”)
  • 全局停止 (STW) – 与并发相反。它在应用程序完全停止时执行垃圾回收。
  • 增量式 以一系列较小的增量执行垃圾回收,各增量之间可能存在较长的间隔。应用程序会在垃圾回收期间停止,但在增量之间运行。
  • 移动式 回收器在垃圾回收过程中移动对象,并且必须更新指向这些存活对象的引用。
  • 保守式 大多数非托管运行时都采用保守式。在这种模型中,回收器无法确定某个字段是否为引用,因此会假定它是引用。这与精确式回收器形成对比。
  • 精确式 精确式回收器能够准确知道每一个可能的对象引用所在位置。如果不是精确式回收器,就无法成为移动式回收器,因为在移动存活对象时,必须知道需要修复哪些引用。

Azul Platform Prime C4 回收器

C4(持续并发压缩回收器)是 Azul Platform Prime 中的默认回收器。C4 会对新生代和老年代进行并发压缩。与其他算法不同,它不是“基本”并发,而是完全并发,因此永远不会回退到全局停止压缩。

C4 使用加载值屏障 (LVB) 在每次加载堆内存引用时验证其正确性。任何指向重定位对象的堆内存引用都会在这个自愈屏障中被捕获和修复。C4 采用一种可保证单次遍历的并发标记器。无论您的应用程序以多快的速度修改堆内存,C4 都能够跟上。C4 回收器还会执行并发引用处理(包括弱引用、软引用和终结引用),并且重定位和重映射也是并发进行的。C4 还使用“快速释放”机制,使释放的内存能迅速供应用程序使用,并用于对象的重定位。这实现了一种无需空闲内存即可工作的“接力式”压缩。

GC 调优观察

对于大多数回收器而言,即便您理解了应用程序的特性,垃圾回收调优仍然很难做到正确。下图展示了 HotSpot JVM 中 CMS 回收器的两组调优参数。尽管它们可能使用了相似的参数,但实际上差异巨大,在某些方面甚至完全相反。然而,根据应用程序的具体特性,任一组参数都可能优化您的应用程序性能。对大多数回收器而言,不存在“放之四海而皆准”的答案。开发人员和架构师必须仔细调优垃圾回收,并在应用程序、环境或预期负载每次发生变化时重新调优。参数设置错误可能会在峰值负载期间引发意外的长时间暂停。

然而,在 Azul Zing C4 回收器上,应用程序性能对“常规”调优参数不敏感。由于它在新生代和老年代中都会进行并发标记和压缩,唯一重要的参数是堆内存大小。

在 Azul Prime 运行时中设置堆内存大小时,C4 回收器会计算为跟上分配速率所需的 GC 时间。您无需设置比例或疏散时间。Zing 的 C4 GC 会在需要时触发,并使用与应用程序线程分离的线程。这使得在最坏的情况下,暂停时间比其他 GC 策略降低了几个数量级。

当 GC 不再成为瓶颈之后,到达安全点的时间成为下一个主要的延迟来源,通过其他控制手段(通常在操作系统层面,或通过 JVM 中的 JIT 编译器调整)还可以实现更低的延迟。

Teal CTA Band

More information

了解 C4 垃圾回收器