1. 概述
Java 21于2023年9月首次亮相,同时引入了分代ZGC。此更新以Z垃圾收集器的效率为基础,重点通过引入新旧对象的单独代来优化内存管理。
在本文中,我们将仔细探讨这一附加功能,探索其潜在好处、其工作原理以及如何使用它。
2. 垃圾收集
为了开始我们的探索,让我们深入研究内存管理领域,垃圾收集是程序尝试释放对象不再使用的已分配内存的过程。如果我们程序的某些部分仍然维护指向某个对象的指针,则该对象被视为“正在使用”或“已引用”。相反,“未使用”或“未引用”对象不再被程序的任何部分访问,从而允许回收它占用的内存。
例如,在Java中,垃圾收集器负责释放堆内存,这是存储Java对象的位置。
这有助于防止内存泄漏并确保有效的资源使用,它还使我们不必手动管理程序的内存,这可能会导致潜在的错误。某些编程语言(例如Java或C#)内置了此功能,而其他编程语言(例如C或C++)可能依赖外部库来实现类似功能。
3. 分代垃圾收集
在内存管理的上下文中,代是指根据对象分配时间对对象进行分类。
让我们将注意力转移到分代垃圾收集上,这代表了一种内存管理策略,其工作原理是根据分配时间将对象划分为不同的代,并根据其代应用不同的方法。
在Java上下文中,内存分为两个主要代:年轻代和老年代。新创建的对象在年轻代中找到自己的位置,在那里频繁进行垃圾收集,超出多个垃圾收集周期的对象将被提升到老年代。这种划分通过承认大多数对象的生命周期较短来优化效率。
有关Java分代垃圾回收的详细信息,请参阅文章Java垃圾回收基础知识。
4. ZGC
Z Garbage Collector,也称为ZGC,是一种可扩展、低延迟的垃圾收集器。它首次在Java 11中引入作为实验性功能,并在Java 15中投入生产。
此功能的目的是最大限度地减少或消除长时间的垃圾收集暂停,从而增强应用程序的响应能力并适应现代系统不断增长的内存容量。
作为一种非分代方法,它将所有对象存储在一起,无论年龄如何,因此每个周期都会收集所有对象。
5. 分代ZGC
分代ZGC旨在提高应用程序性能,通过为新对象和旧对象维护单独的代来扩展现有的ZGC。
5.1 动机
对于大多数用例,ZGC足以解决与垃圾收集相关的延迟问题。只要有足够的可用资源来确保垃圾收集器回收内存的速度快于我们的程序消耗内存的速度,这种方法就很有效。
但是,弱分代假说指出大多数对象都会在年轻时死亡。因此,从这些短命的年轻资源中收集和回收内存需要更少的计算资源,此过程会快速解锁更多内存。
另一方面,收集已经经历了多个周期并具有更长寿命的旧对象需要更多的计算资源。然而,通过收集旧对象释放的内存量相对较少。事实证明,该策略在快速释放内存方面更加有效,有助于提高整体应用程序性能。
5.2 目标
与非分代ZGC相比,分代ZGC旨在提供一些关键优势:
- 减少分配停滞的风险
- 减少堆内存开销要求
- 降低垃圾收集CPU开销
此外,我们的目标是增加这些优势,同时保留使用非分代方法已有的优势:
- 暂停时间低于1毫秒
- 支持高达数TB的堆大小
- 最少的手动配置
为了维持最后一点,新的GC不需要任何手动配置代的大小、使用的线程数或对象应在年轻代中驻留多长时间。
5.3 描述
分代ZGC引入了两代堆结构:年轻代用于最近的对象,老年代用于长期对象。每一代都是独立收集的,优先收集年轻对象。
并发收集与非分代ZGC类似,依赖于染色指针、加载屏障和存储屏障来实现一致的对象图视图。染色指针包含元数据,有助于高效的64位对象指针的使用。加载屏障解释元数据,而存储屏障处理元数据添加、维护记忆集并将对象标记为活动对象。
5.4 启用分代ZGC
为了实现平稳过渡,分代ZGC将与非分代ZGC一起使用。-XX:UseZGC命令行选项将选择非分代ZGC。要选择分代ZGC,我们需要添加-XX:+ZGenerational选项:
java -XX:+UseZGC -XX:+ZGenerational ...
分代ZGC旨在成为未来Java版本中的默认ZGC,此外,在更晚的版本中,非分代ZGC可能会被完全删除。
5.5 风险
新GC中屏障和染色指针的集成引入了更高的复杂性,超越了非分代GC。分代ZGC还同时运行两个垃圾收集器,这些收集器并不完全独立,因为它们在某些情况下会交互,从而增加了实现的复杂性。
尽管预计它在大多数用例中都会表现出色,但某些工作负载会带来性能轻微下降的风险。为了解决这个问题,分代ZGC的持续发展和优化将由基准和用户反馈驱动,旨在随着时间的推移解决和减轻这些已识别的风险。
6. 各代ZGC设计差异
分代ZGC引入了一些设计差异,与非分代ZGC相比,提高了垃圾收集效率和用户适应性。
6.1 通过优化屏障增强性能
分代ZGC放弃多映射内存,转而使用加载和存储屏障内的显式代码。为了适应存储屏障和修订后的负载屏障责任,分代ZGC采用高度优化的屏障代码。利用快速路径和慢速路径等技术,优化的屏障即使在密集工作负载下也能确保应用程序的最大吞吐量和性能。
6.2 高效的代间指针跟踪
双缓冲记忆集(为每个老年代区域成对组织)使用位图来有效跟踪代间指针,这种设计选择促进了应用程序和垃圾收集线程的并发工作,而不需要额外的内存屏障,从而使执行更顺畅。
6.3 优化的年轻代集合
通过分析年轻代区域的密度,分代ZGC选择性地疏散区域,减少年轻代收集所需的工作量。此优化有助于实现更快、更高效的垃圾收集周期,从而提高应用程序响应能力。
6.4 灵活处理大型物体
分代ZGC允许将大型对象分配给年轻代,从而引入了处理大型对象的灵活性。这消除了对老年代的抢占分配的需要,从而提高了内存效率。现在,如果生命周期较短,则可以在年轻代中收集大型对象;如果生命周期较长,则可以有效地将其提升到老年代。
7. 总结
正如我们在本文中了解到的,Java 21具有一个强大的功能,即分代ZGC。通过仔细考虑潜在风险并致力于根据用户反馈进行持续改进,预计将提供更高的效率和响应能力,使其成为Java不断发展的生态系统的宝贵补充。