Elasticsearch 的 JVM 基础知识:指标、内存和监控

Elasticsearch 是一个基于 Java 的搜索和分析引擎以及向量数据库,构建于 Apache Lucene 之上,并且是 Elasticsearch Platform 的核心。要在其支持的 Platform 上运行 Elasticsearch,您需要一个 Java 虚拟机 (JVM)。JVM 提供了一个独立于Platform的运行时环境,您可以在现有操作系统之上的虚拟环境中运行 Elasticsearch。JVM 对底层操作系统和硬件进行了抽象,因此 Java 应用程序可以在任何 Platform 上运行。
了解 JVM 的内存管理以及通过垃圾回收 (GC) 进行的对象回收,是排查 java.lang.OutOfMemoryError(退出代码 127)等问题的关键。当 JVM 运行时内存耗尽或出现退出代码 137 时,就会发生这些问题,这通常表明主机的内存不足 (OOM) 杀手因内存占用过高而终止了 JVM 进程。本博客将指导您如何检查内存使用模式并排查这些 JVM 问题。
我还将解释 JVM 的作用,以及如何借助现有的 Elasticsearch API 将其关联起来,从而为您提供有关 Elasticsearch JVM 状态的宝贵见解,并帮助您决定是否需要根据自己的用例对其进行进一步调整。
正如Elasticsearch JVM 文档中所述,产品发送/传输的 JVM 选项默认值在所有受支持的生产环境中都能高效运行;这些默认值可以处理搜索和索引操作的大多数用例。不建议更改任何值或为 JVM 选项或设置添加任何自定义值。
如果您认为任何更改对您的工作负载有益,请在执行此操作之前联系 Elastic 支持。
什么是 JVM?
Java 虚拟机是 Java Runtime Environment (JRE) 的组成部分,该环境随 Java Development Kit (JDK) 一起提供。

JVM 类似于一个有助于运行 Java 应用程序的计算机程序。它并非以物理机器的形式存在,但它通过将 Java 代码转换为受支持的操作系统能够理解的指令,从而起到机器的作用。JVM 还负责处理内存管理、执行垃圾回收以及确保应用程序安全等重要任务。
接下来,让我们重点关注 JVM 内存管理和垃圾回收——这是 Elasticsearch 中的一个重点,我们在排查任何内存性能或 OOM 相关问题时都会经常检查。
要了解内存管理和垃圾回收,首先需要了解 JVM 启动时所有对象驻留的堆区域(或堆内存)的逻辑表示。
了解 JVM 堆内存:年轻代和老年代
堆区域或堆内存是内存池的组合,对象根据其生命周期和年龄存活于其中。JVM 堆内存主要由年轻代和老年代组成。年轻代进一步划分为 Eden 区和 Survivor 区,如下图所示。Java 对象根据其年龄和引用被分配到这些区域,然后从年轻代区域移动到老年代区域。
Eden 空间中大部分的堆利用率都会被迅速回收。任何未被释放的堆都会移至其中一个 survivor 空间(S0 或 S1),在那里它通常会遵循指数衰减模式被回收。如果它在此之后仍然存在,则会移至 Tenured 空间。
JVM 堆内存合计 = 年轻代(Eden 区以及 Survivor 区 S0 和 S1)+ 老年代(Tenured)

年轻代区域
当任何 Java 应用程序启动时,它都会创建新对象并将这些新对象分配到称为堆内存的区域中。年轻代的 Eden 空间是应用程序分配新创建对象的第一个位置。该区域专用于分配新对象。当它变满时,符合条件的对象将被回收(垃圾回收)或提升到其他区域。
当发生次要垃圾回收时,被引用的对象将被移动到另一个子部分,即 Survivor 区域 S0。未被引用的对象将被删除,以释放 Eden 中的空间。该区域包含在垃圾回收过程中幸存的对象,因此得名Survivor 区域。
当 Eden 空间再次满载时,同样的循环会重复进行。这一次,在 Eden 和 S0 中存活下来的对象将被移动到 S1 区域,此过程将持续进行。
在 Eden、S0 和 S1 中经历垃圾回收后存活下来的对象,将根据年龄计算器被提升至旧代。
老年代区域/区域
老年代(也称为年老代)是长寿命对象的集合,这些对象在年轻代(小型 GC)的多次垃圾回收事件中存活了下来。在幸存者空间中经历了一定数量的周期后仍存活的对象,会被算法移动到老年代,这也被称为 对象晋升。

来自 Stack Monitoring 的上述锯齿状 GC 模式显示,当发生次要垃圾回收时,JVM 堆内存会减少,因为 GC 正在从内存中移除不需要的对象。经过几个周期后,对象会累积,GC 会再次启动以清除这些对象。
垃圾回收方法
Elasticsearch 发送/传输了受支持的 捆绑 JDK 版本,详细信息记录在发行说明中。在 v6.4 之前,Elasticsearch 一直使用并发标记清除 (CMS) 垃圾收集器,该收集器在 JDK9 中已被弃用。Elasticsearch 现在使用 Garbage-First (G1) 垃圾收集器作为默认的 GC 方法。与 CMS GC 方法相比,它也具有更好的性能。G1GC 的内部机制很复杂,涉及许多阶段,但让我们来剖析一下它的基础知识。
要使用 G1GC,请在 Elasticsearch JVM 选项中使用 JVM 标志 -XX:+UseG1GC。在 G1GC 中,总堆区域将被划分为大小相等的区域 -XX:G1HeapRegionSize=1m,这些区域的大小可以根据堆大小在 1 MB 到 32 MB 之间变化。Eden、Survivor 和老年代是这些区域的逻辑集合,且不是连续的。除此之外,堆区域内还有大对象 (humongous) 区域和空闲/可用区域。大对象区域用于分配超过区域大小一半的对象。

G1GC 具有一个暂停时间目标(-XX:MaxGCPauseMillis=200),它会在垃圾回收期间尝试达到该目标。这意味着在垃圾回收期间,它确保 JVM 不会经历超过此值的暂停,这使其优于 CMS 或其他没有此选项的回收器。总体而言,建议将 GC 设置保持为默认值,这将使 GC 暂停保持在限制范围内,从而提高性能。
用于 JVM 状态的 Elasticsearch API
Elasticsearch 想要执行的任何任务都需要请求 JVM 分配一定的内存,以便执行该任务。如上所述,JVM 代表 Elasticsearch 处理内存管理,因此它无需为此操心。不过,管理员可能希望通过 Elasticsearch API 查看 JVM 状态,以确保各项工作按预期进行。
要检查已指定或作为输入参数提供给 Elasticsearch JVM 的所有设置,请使用带有 JVM 属性的 node info API(也在文档中进行了说明)。
GET _nodes/_all/jvm 此 API 将提供 Node 配置的详细信息,包括 JVM 输入参数、内存池和堆大小。
此外,如果您想查看特定 Node 运行时间、所有内存池中使用的堆内存,或有关垃圾回收的统计信息,以了解 Elasticsearch JVM 的指标,您可以使用Node 统计信息 API。它将提供有关上述各项指标的详细度量数据。
GET _nodes/stats/jvm
or
GET _nodes/stats/jvm?filter_path=nodes.*.jvm注意:Elasticsearch 不建议根据从 Node stats API 获取的 heap_percent 值来做决策。我们的 UI 会计算内存压力,用户应根据此博客中描述的计算方法采取必要措施。
使用内置 JDK 工具检查 JVM 指标
如果您是高级用户,并且希望在不使用 Elasticsearch API 的情况下查看正在运行的 JVM 的实时统计信息,则可以使用 bundled java 并运行 jstat 来查看实时统计数据。为此,请先找到 Java 主目录或 bin 目录以及 Elasticsearch 进程的 PID。在 bin 目录下,使用 jstat 工具:
./jstat -gcutil <PID> 2000此处,2000 是获取统计信息并将其打印到 CLI 后的时间间隔(以毫秒为单位)。此命令有助于识别所有区域中 GC 的发生频率,以及内存池被对象填满的速度(对象创建和提升速率)。
这并非上述 API 的替代方案,而是更多用于开发者级别的调试。
总结一下吧!
在本篇博文中,我们介绍了 Java 虚拟机 (JVM) 的基本概念、各种内存池、垃圾回收的重要性,以及如何查看 Elasticsearch JVM 统计信息。在下一篇博文中,我们将讨论重要的 JVM 设置以及如何在 Elasticsearch 中轮询其当前指标。
如果您有任何疑问或需要进一步的支持,请随时与我们联系。我们将随时竭诚为您提供帮助!
本文中描述的任何功能或功能性的发布和时间均由 Elastic 自行决定。当前尚未发布的任何功能或功能性可能无法按时提供或根本无法提供。