繁体   English   中英

Ignite DataStreamer可能存在内存泄漏

[英]Possible Memory Leak in Ignite DataStreamer

我在启用了持久性的Kubernetes集群中运行Ignite。 每台机器的Java Heap为24GB,20GB专用于持久内存,内存限制为110GB。 我的相关JVM选项是-XX:+AlwaysPreTouch -XX:+UseG1GC -XX:+ScavengeBeforeFullGC 在每个节点上运行DataStreamers几个小时后,我的集群上的节点达到了k8s内存限制,触发了OOM kill。 在运行Java NMT之后,我惊讶地发现分配给内部存储器的大量空间。

Java Heap (reserved=25165824KB, committed=25165824KB)
(mmap: reserved=25165824KB, committed=25165824KB)  

Internal (reserved=42425986KB, committed=42425986KB)
(malloc=42425954KB #614365) 
(mmap: reserved=32KB, committed=32KB) 

Kubernetes指标证实了这一点:

在此输入图像描述

“Ignite Cache”是内核页面缓存。 最后一个面板“Heap + Durable + Buffer”是点燃指标HeapMemoryUsed + PhysicalMemorySize + CheckpointBufferSize的总和。

我知道这不是数据建立的结果,因为DataStreamers在他们读取的每个文件之后刷新(最多大约250MB),并且没有节点一次读取超过4个文件。 在我结束排除其他问题之后,我尝试设置-XX:MaxDirectMemorySize=10G ,并调用手动GC,但除了定期关闭所有pod并重新启动它们之外似乎没有任何影响。

我不知道从哪里开始。 Ignite中有一种解决方法,它不会强迫我使用第三方数据库吗?

编辑:我的DataStorageConfiguration

    <property name="dataStorageConfiguration">
        <bean class="org.apache.ignite.configuration.DataStorageConfiguration">
            <property name="metricsEnabled" value="true"/>
            <property name="checkpointFrequency" value="300000"/>
            <property name="storagePath" value="/var/lib/ignite/data/db"/>
            <property name="walFlushFrequency" value="10000"/>
            <property name="walMode" value="LOG_ONLY"/>
            <property name="walPath" value="/var/lib/ignite/data/wal"/>
            <property name="walArchivePath" value="/var/lib/ignite/data/wal/archive"/>               
            <property name="walSegmentSize" value="2147483647"/>
            <property name="maxWalArchiveSize" value="4294967294"/>
            <property name="walCompactionEnabled" value="false"/>
            <property name="writeThrottlingEnabled" value="False"/>
            <property name="pageSize" value="4096"/>                
            <property name="defaultDataRegionConfiguration">
                <bean class="org.apache.ignite.configuration.DataRegionConfiguration">
                    <property name="persistenceEnabled" value="true"/>
                    <property name="checkpointPageBufferSize" value="2147483648"/>
                    <property name="name" value="Default_Region"/>
                    <property name="maxSize" value="21474836480"/>
                    <property name="metricsEnabled" value="true"/>
                </bean>
            </property>
        </bean>
    </property> 

更新:当我禁用持久性时,内部存储器被正确处理:

在此输入图像描述

更新: 此处以可重现的示例演示该问题。 它可以在一台机器上运行,该机器至少有22GB内存用于docker和大约50GB的存储空间。 有趣的是,当传入字节数组或字符串作为值时,泄漏才真正引人注目。

TLDR

设置walSegmentSize=64mb (或者只删除设置并使用默认设置)并设置-XX:MaxDirectMemorySize=<walSegmentSize * 4>

说明

人们在计算Ignite的内存需求时经常忘记的一件事是直接内存缓冲区大小。

直接内存缓冲区是从Java进程中的单独空间分配的JVM管理缓冲区 - 它既不是Java堆,也不是Ignite数据区域或Ignite检查点缓冲区。

直接内存缓冲区是与Java中的非堆内存交互的常规方式。 有很多东西使用它(从JVM的内部代码到应用程序),但在Ignite服务器中,直接内存池的主要用户是预写日志。

默认情况下,Ignite使用内存映射文件写入WAL,该文件通过直接内存缓冲区工作。 该缓冲区的大小是WAL段的大小。 在这里,我们得到了有趣的东西。

你的WAL段很大! 2GB - 很多。 默认值是64mb,我很少看到一个使用更多的环境。 在某些特定工作负载和某些特定磁盘中,我们建议设置256mb。

因此,您在直接内存池中创建了2GB缓冲区。 默认情况下,直接内存的最大大小等于-Xmx - 在您的情况下为24GB。 我可以看到直接内存池膨胀到24GB(来自尚未清除的旧缓冲区)的情况,使应用程序的总大小至少为20 + 2 + 24 + 24 = 70GB

这解释了40GB的内部JVM内存(我认为这是数据区域+直接)。 这也解释了为什么在持久性关闭时没有看到问题 - 在这种情况下你没有WAL。

该怎么办

  1. 选择一个理智的walSegmentSize 我不知道2GB选择背后的原因,但如果你确定你有小WAL段的问题,我建议你去默认64mb或256mb。

  2. 通过-XX:MaxDirectMemorySize=<size>为JVM的直接内存池设置限制。 我发现将它设置为walSegmentSize * 4的值是安全的选择,即在256mb-1gb范围内的某个位置。

即使您在进行上述更改后发现内存消耗问题 - 仍然保留它们,因为它们是99%群集的最佳选择。

内存泄漏似乎是由我的缓存模型中的值对象上的@QueryTextField注释触发的,该注释支持Ignite中的Lucene查询。

最初: case class Value(@(QueryTextField@field) theta: String)

将此行更改为: case class Value(theta: String)似乎可以解决问题。 我没有解释为什么这有效,但也许对Ignite代码库有很好理解的人可以解释原因。

我不知道你的情况是什么“内部”,但Ignite通常会将其所有数据存储在Off-Heap内存中。 请注意,它也不是“直接”内存。

您可以配置专用于Off-Heap的内存量 ,以及配置Page Eviction

启用和不启用持久性,我可以看到图表中的点火缓存指标存在巨大差距。 这意味着,通过持久性,您实际上是将数据写入数据存储目录wal,walArchive。 如果Kubernetes pod也在考虑内存限制中的目录,那么它可能很快就会耗尽内存。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM