Java最高

通过。开始使用Spring 5和Spring Boot 2学习的春天课程:

>>查看课程

1.介绍

今天,应用程序同时服务数千甚至数百万用户的情况并不少见。这样的应用程序需要大量的内存。然而,管理所有这些内存可能很容易影响应用程序的性能。

为了解决这个问题,Java 11引入了Z垃圾收集器(ZGC)作为一个实验性的垃圾收集器(GC)实现。

在本教程中,我们将看到ZGC如何在甚至是多tb的堆上保持低暂停时间

2.主要概念

要理解ZGC是如何工作的,我们需要理解其背后的基本概念和术语内存管理垃圾收集器

2.1。内存管理

物理内存是我们的硬件提供的RAM。

操作系统为每个应用分配虚拟内存空间。

当然,我们将虚拟内存存储在物理内存中,操作系统负责维护两者之间的映射关系。这种映射通常涉及到硬件加速。

2.2。Multi-Mapping

多映射意味着在虚拟内存中有特定的地址,这些地址指向物理内存中的相同地址。因为应用程序通过虚拟内存访问数据,所以它们不知道这种机制(也不需要知道)。金宝搏官网188be

有效地,我们将虚拟内存的多个范围映射到物理内存中的同一个范围:

乍一看,它的用例并不明显,但是我们稍后会看到,ZGC需要它来发挥它的魔力。此外,它还提供了一定的安全性,因为它将应用程序的内存空间分开。

2.3。搬迁

由于我们使用动态内存分配,平均应用程序的内存随着时间的推移会变成碎片。这是因为当我们在内存中释放一个对象时,会留下一个空闲空间缺口。随着时间的推移,这些间隙不断累积,我们的记忆就会像棋盘一样,由空闲空间和已使用空间交替组成。

当然,我们可以尝试用新的对象来填补这些空白。要做到这一点,我们应该扫描内存中足够容纳对象的空闲空间。这样做是一个昂贵的操作,特别是当我们每次想要分配内存时都必须这样做的时候。此外,内存仍然是碎片,因为我们可能无法找到一个具有我们需要的精确大小的空闲空间。因此,物体之间会有间隙。当然,这些差距变小了。同样,我们可以尽量减少这些差距,但它会使用更多的处理能力。

另一个策略是经常使用以更紧凑的格式将对象从碎片内存区域重定位到空闲区域。为了更有效,我们将内存空间分割为块。我们重新定位一个块中的所有对象,或者一个都不重新定位。这样,内存分配将会更快,因为我们知道内存中有完整的空块。

2.4。垃圾收集

当我们创建Java应用程序时,我们不必释放我们分配的内存,因为垃圾收集器会为我们做这件事。总之,GC通过引用链来监视应用程序中哪些对象可以访问,并释放那些不能访问的对象

GC需要跟踪堆空间中对象的状态来完成它的工作。例如,一个可能的状态是可达的。这意味着应用程序持有对该对象的引用。这个引用可以是传递的。唯一重要的是应用程序可以通过引用访问这些对象。另一个例子是finalize:我们不能访问的对象。这些就是我们认为是垃圾的东西。

为了实现这个目标,垃圾收集器有多个阶段。

2.5。GC阶段属性

GC阶段可以有不同的属性:

  • 一个平行phase可以在多个GC线程上运行
  • 一个串行phase运行在单个线程上
  • 一个停止一切阶段不能与应用程序代码同时运行
  • 一个并发phase可以在后台运行,而我们的应用程序执行它的工作
  • 一个增量阶段可以在完成所有工作之前终止,并在以后继续

请注意,上述所有技术都有各自的优缺点。例如,假设我们有一个阶段可以与我们的应用程序并发运行。此阶段的串行实现需要总体CPU性能的1%并运行1000毫秒。相比之下,并行实现利用30%的CPU并在50ms内完成它的工作。

在这个例子中,并行解决方案总体上使用更多的CPU,因为它可能更复杂,并且必须同步线程。对于CPU占用较多的应用程序(例如批处理作业),这是一个问题,因为我们的计算能力较低,无法完成有用的工作。

当然,这个例子是虚构的数字。但是,很明显,所有应用程序都有它们的特征,因此它们有不同的GC需求。

欲了解更多详情,请访问我们关于Java内存管理的文章

3.动作概念

ZGC打算提供尽可能短的停止世界阶段。它的实现方式是,这些暂停时间的持续时间不会随着堆大小而增加。这些特性使ZGC非常适合服务器应用程序,在这些应用程序中,经常出现大堆,并且需要快速的应用程序响应时间。

在经过试验和测试的GC技术之上,ZGC引入了一些新概念,我们将在以下几节中介绍这些概念。

但是现在,让我们来看看ZGC是如何工作的。

3.1。大图片

ZGC有一个称为标记的阶段,在这个阶段我们找到可到达的对象。GC可以以多种方式存储对象状态信息。例如,我们可以创建一个地图,其中键是内存地址,值是该地址处对象的状态。它很简单,但需要额外的内存来存储这些信息。而且,维护这样的地图也很有挑战性。

ZGC使用一种不同的方法:它将引用状态存储为引用的位。这叫做参考染色。但这样我们就有了新的挑战。设置一个引用的位来存储关于一个对象的元数据意味着多个引用可以指向同一个对象,因为状态位不包含金宝搏官网188be关于对象位置的任何信息。多手劫持来救援!

我们还想减少内存碎片。ZGC使用重定位来实现这一点。但是对于较大的堆,重定位是一个缓慢的过程。因为ZGC不需要长暂停时间,所以它与应用程序并行地执行大部分的重定位。但这也带来了一个新问题。

假设我们有一个对象的引用。ZGC重新定位该对象,并发生上下文切换,此时应用程序线程运行并试图通过其旧地址访问该对象。ZGC使用负载障碍来解决这个问题。加载障碍是一段在线程从堆加载引用时运行的代码—例如,当我们访问对象的非原语字段时。

在ZGC中,负载屏障检查引用的元数据位。根据这些位元,在我们得到引用之前,ZGC可能会对它进行一些处理。因此,它可能产生一个完全不同的引用。我们称之为重映射。

3.2。标记

ZGC将标记分为三个阶段。

第一阶段是“停止世界”阶段。在这个阶段,我们查找根引用并标记它们。根引用是到达堆中的对象的起点例如,局部变量或静态字段。因为根引用的数量通常很少,所以这个阶段很短。

下一个阶段是并发的。在此阶段,我们从根引用开始遍历对象图。我们标记我们所到达的每一个目标。同样,当load barrier检测到一个未标记的引用时,它也会标记它。

最后一个阶段也是一个停止世界阶段,用于处理一些边缘情况,如弱引用。

在这一点上,我们知道我们可以到达哪些物体。

使用动作marked0marked1用于标记的元数据位。

3.3。参考颜色

引用表示一个字节在虚拟内存中的位置。然而,我们并不一定要使用引用的所有位来做到这一点一些位可以表示引用的属性。这就是我们所说的参考色彩。

用32位元,我们可以处理4千兆字节。由于现在计算机拥有比这更多的内存是很普遍的,我们显然不能使用这32位中的任何一个来着色。因此,ZGC使用64位引用。它的意思是ZGC仅适用于64位平台:

ZGC引用使用42位来表示地址本身。因此,ZGC引用可以寻址4tb的内存空间。

在那之上,我们有4位存储参考状态:

  • 可终结位—对象只能通过终结器访问
  • 重新映射位——引用是最新的,指向对象的当前位置(参见relocation)
  • marked0marked1位——这些位用来标记可到达的对象

我们也称这些位元数据位。在ZGC中,这些元数据中只有一个是1。

3.4。搬迁

在ZGC中,搬迁包括以下几个阶段:

  1. 并发阶段,查找块,我们想重定位并将它们放入重定位集中。
  2. 停止世界阶段重定位重定位集中的所有根引用并更新它们的引用。
  3. 并发阶段对重定位集中的所有剩余对象进行重定位,并将新旧地址之间的映射存储在转发表中。
  4. 在下一个标记阶段,重写其余的引用。这样,我们就不必遍历对象树两次。或者,负载屏障也可以做到这一点。

3.5。重新映射和加载障碍

注意,在重定位阶段,我们没有重写对重定位地址的大多数引用。因此,使用这些引用,我们无法访问我们想要访问的对象。更糟糕的是,我们可能会接触到垃圾。

ZGC使用负载障碍来解决这个问题。负载障碍使用一种称为重新映射的技术修复指向已迁移对象的引用。

当应用程序加载引用时,它会触发load barrier,然后按照以下步骤返回正确的引用:

  1. 检查是否重新映射bit设置为1。如果是,这意味着引用是最新的,所以我们可以安全地返回它。
  2. 然后我们检查被引用的对象是否在重定位集中。如果不是,那就意味着我们不想搬家。为了避免下次加载引用时出现这种检查,我们设置重新映射位到1并返回更新后的引用。
  3. 现在我们知道我们要访问的对象是重定位的目标。唯一的问题是搬迁是否发生了?如果对象已被重新定位,我们将跳转到下一步。否则,我们现在重新定位它,并在转发表中创建一个条目,该条目存储每个重定位对象的新地址。在这之后,我们继续下一步。
  4. 现在我们知道物体被转移了。或者通过ZGC,或者在前面的步骤中通过us,或者在此对象之前的碰撞过程中通过load barrier。我们将这个引用更新为对象的新位置(使用上一步中的地址或通过在转发表中查找),设置重新映射位,并返回引用。

就是这样,通过上面的步骤,我们确保了每次尝试访问一个对象时,我们都会得到对它的最新引用。因为每次我们加载一个引用时,它都会触发加载障碍。因此,它会降低应用程序的性能。特别是我们第一次访问一个重新定位的对象时。但如果我们想要短暂的暂停,这是我们必须付出的代价。由于这些步骤相对较快,因此不会显著影响应用程序的性能。

4.如何启用ZGC?

当运行我们的应用程序时,我们可以使用以下命令行选项来启用ZGC:

- xx: + UnlockExperimentalVMOptions - xx: + UseZGC

请注意,由于ZGC是一个实验性的GC,它需要一些时间才能得到官方的支持。

5.结论

在本文中,我们看到ZGC打算支持具有较低应用程序暂停时间的大堆大小。

为了达到这个目标,它使用了一些技术,包括64位彩色引用、负载障碍、重定位和重映射。

Java底部

通过。开始使用Spring 5和Spring Boot 2学习的春天课程:

>>查看课程
本文评论关闭!