JVM 设置

编辑

jvm.options 设置文件中配置 JVM 设置。JVM 设置也可以通过 LS_JAVA_OPTS 环境变量进行设置。

此文件包含一个以换行符分隔的 JVM 参数列表,遵循以下特殊语法

  • 仅包含空格的行将被忽略
  • # 开头的行被视为注释,将被忽略

    # this is a comment
  • - 开头的行被视为 JVM 选项,该选项独立于 JVM 版本应用

    -Xmx2g
  • 以数字开头,后跟 :,再后跟 - 的行被视为 JVM 选项,该选项仅在 JVM 版本与该数字匹配时应用

    8:-Xmx2g
  • 以数字开头,后跟 -,再后跟 : 的行被视为 JVM 选项,该选项仅在 JVM 版本大于或等于该数字时应用

    8-:-Xmx2g
  • 以数字开头,后跟 -,再后跟数字,再后跟 : 的行被视为 JVM 选项,该选项仅在 JVM 版本落在两个数字的包含范围内时应用

    8-9:-Xmx2g
  • 所有其他行将被拒绝

设置内存大小

编辑

执行 Logstash 的 JVM 内存可以分为两个区域:堆内存和非堆内存。堆内存指的是 Java 堆,其中包含 Logstash 在运行期间创建的所有 Java 对象,有关如何调整其大小的描述,请参阅设置 JVM 堆大小。不属于堆内存的部分称为非堆内存,包括 Logstash 可以使用和控制的内存,通常是线程栈、直接内存和内存映射页面,有关全面描述,请查看设置非堆大小。在非堆空间中,有一些空间被 JVM 使用,其中包含虚拟机执行的所有数据结构。此内存无法由 Logstash 控制,并且很少自定义设置。

设置 JVM 堆大小

编辑

以下是一些调整 JVM 堆大小的提示

  • 对于典型的摄取场景,建议的堆大小应不小于 4GB 且不大于 8GB。
  • 如果堆大小太小,CPU 利用率可能会不必要地增加,从而导致 JVM 不断进行垃圾回收。您可以将堆大小加倍来检查是否存在此问题,以查看性能是否有所提高。
  • 不要将堆大小增加到超出物理内存的量。必须保留一些内存来运行操作系统和其他进程。对于大多数安装,一般准则是不超过物理内存的 50-75%。您拥有的内存越多,您可以使用的百分比越高。
  • 将最小 (Xms) 和最大 (Xmx) 堆分配大小设置为相同的值,以防止堆在运行时调整大小,这是一个非常耗费资源的过程。
  • 您可以使用 Java 随附的 jmap 命令行实用程序或使用 VisualVM 来更准确地测量 JVM 堆。有关详细信息,请参阅分析堆

设置非堆大小

编辑

除了分配给堆大小的内存外,操作系统、持久化队列 mmap 页面、直接内存和其他进程也需要内存。

内部 JVM 数据结构、线程栈、内存映射文件以及用于输入/输出 (IO) 操作的直接内存都是非堆 JVM 内存的一部分。内存映射文件不属于 Logstash 进程的非堆内存,但在从磁盘分页文件时会消耗 RAM。这些映射文件加快了对持久化队列页面的访问速度,这是一种性能改进或权衡,旨在减少昂贵的磁盘操作(如读取、写入和寻道)。一些网络 I/O 操作还诉诸于进程内的直接内存使用,以避免例如在网络套接字之间复制缓冲区。诸如 Elastic Agent、Beats、TCP 和 HTTP 输入之类的输入插件使用直接内存。线程栈区域包含 JVM 创建的每个 Java 线程的栈帧列表;每个帧都保留在方法调用期间传递的局部参数。如果需要调整大小以满足处理需求,请阅读设置 JVM 栈大小

插件根据其类型(输入、过滤器和输出)具有不同的线程模型。每个输入插件都在其自己的线程中运行,并且可能会产生其他线程。例如,每个 JDBC 输入插件都会启动一个调度程序线程。基于 Netty 的插件(如 TCP、Beats 或 HTTP 输入)会生成一个包含 2 * number_of_cores 线程的线程池。输出插件也可能会启动辅助线程,例如每个 Elasticsearch 输出实例的连接管理线程。每个管道也有其自己的线程负责管理管道生命周期。

总而言之,我们有 3 类内存使用情况,其中 2 类可以由 JVM 限制,另一类依赖于可用的空闲内存

内存类型 使用以下配置 由以下使用

JVM 堆

-Xmx

任何正常的对象分配

JVM 直接内存

-XX:MaxDirectMemorySize

beats、tcp 和 http 输入

本机内存

不适用

持久化队列页面、线程栈

在计算理想的内存分配时,请记住这些内存要求。

即将到来的缓冲区分配更改和故障排除内存不足错误

编辑

诸如 Elastic Agent、Beats、TCP 和 HTTP 输入之类的插件,当前默认使用直接内存,因为它往往可以提供更好的性能,尤其是在与网络堆栈交互时。在高负载下,即大量连接和大型消息,直接内存空间可能会耗尽,从而导致非堆空间中出现内存不足 (OOM) 错误。

非堆 OOM 很难调试,因此 Logstash 在 logstash.yml 中提供了一个 pipeline.buffer.type 设置,使您可以控制为使用内存缓冲区的插件分配内存的位置。当前,它默认设置为 direct,但是您可以将其更改为 heap 来改为使用 Java 堆空间,这将成为将来的默认设置。当设置为 heap 时,插件使用的缓冲区分配被配置为首选 Java 堆而不是直接内存,因为根据插件的不同,可能仍然需要直接内存分配。

当设置为“堆”时,如果发生内存不足,Logstash 将生成堆转储以方便调试。

请务必注意,Java 堆大小调整要求将受到此更改的影响,因为以前驻留在直接内存中的分配将改为使用堆。

从性能角度来看,不应该有明显的冲击,因为虽然直接内存 IO 更快,但这些插件生成的 Logstash Event 对象最终会被分配到 Java 堆上,无论设置如何,都会产生从直接内存复制到堆内存的成本。

  • 当您将 pipeline.buffer.type 设置为 heap 时,请考虑将 Java 堆增加已为直接空间保留的内存量。

内存大小调整

编辑

必须估算总 JVM 内存分配,并通过 Java 堆和直接内存设置间接控制。默认情况下,JVM 的非堆直接内存限制与堆大小相同。查看beats 输入内存使用。考虑将 -XX:MaxDirectMemorySize 设置为堆大小的一半或可以容纳您期望这些插件处理的负载的任何值。

在进行容量计算时,请记住 JVM 无法消耗主机可用内存的总量,因为操作系统和其他进程也需要内存。

对于在多个管道上启用持久化队列 (PQ) 的 Logstash 实例,我们可以使用以下公式估算内存消耗

pipelines number * (pipeline threads * stack size + 2 * PQ page size) + direct memory + Java heap

每个持久化队列都要求至少在内存中存在并可访问头部和尾部页面。默认页面大小为 64 MB,因此每个 PQ 至少需要 128 MB 的堆内存,这可能是每个管道内存消耗的重要来源。请注意,内存映射文件的大小不能用上限限制。

栈大小是取决于所用 JVM 的设置,但可以使用 -Xss 设置进行自定义。

默认情况下,直接内存空间与 Java 堆一样大,但可以使用 -XX:MaxDirectMemorySize 设置进行自定义。

示例

考虑一个运行 10 个管道的 Logstash 实例,其中包含不启动其他线程的简单输入和输出插件,它有 1 个管道线程、1 个输入插件线程和 12 个工作线程,总共 14 个。请记住,默认情况下,JVM 分配的直接内存等于为 Java 堆分配的内存。

计算结果为

  • 本机内存:1.4Gb [来自 10 * (14 * 1Mb + 128Mb)]
  • 直接内存:4Gb
  • Java 堆:4Gb

设置 JVM 栈大小

编辑

大型配置可能需要额外的 JVM 栈内存。如果您看到堆栈溢出错误,请尝试增加 JVM 栈大小。在 jvm.options 设置文件中添加类似于此的条目

-Xss4M

请注意,默认栈大小在每个平台和每个操作系统版本上都不同。您可以通过运行以下命令找出默认值

java -XX:+PrintFlagsFinal -version | grep ThreadStackSize

根据默认栈大小,从乘以 4 倍开始,然后是 8 倍,然后是 16 倍,直到溢出错误解决。

使用 LS_JAVA_OPTS

编辑

LS_JAVA_OPTS 环境变量还可以用于覆盖 jvm.options 文件 设置文件中的 JVM 设置。此变量的内容会添加到 jvm.options 文件中配置的选项中,并且会覆盖两个地方都存在的任何设置。

例如,要设置不同的区域设置来启动 Logstash 实例

LS_JAVA_OPTS="-Duser.country=DE -Duser.language=de" bin/logstash -e 'input { stdin { codec => json } }'