Ignite DataStreamer 中可能存在的内存泄漏断续器解释该怎么办

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

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指标证实了这一点:

enter image description here

“Ignite Cache”是内核页面缓存。最后一个面板“堆 + 持久 + 缓冲区”是点火指标 + + 的总和。HeapMemoryUsedPhysicalMemorySizeCheckpointBufferSize

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

我不知道该何去何从。Ignite 中是否有不强制我使用第三方数据库的解决方法?

编辑:我的数据存储配置

    <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> 

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

enter image description here

更新:此处通过可重现的示例演示了此问题。它可以在至少具有22GB的docker内存和大约50GB存储空间的计算机上运行。有趣的是,只有在传入字节数组或字符串作为值时,泄漏才真正明显。


答案 1

断续器

设置(或直接删除设置并使用默认值)和 设置 。walSegmentSize=64mb-XX:MaxDirectMemorySize=<walSegmentSize * 4>

解释

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

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

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

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

您的WAL细分市场是巨大的!2GB - 这是很多。默认值为64mb,我很少看到一个环境会使用超过这个值。在某些特定工作负载和某些特定磁盘中,我们建议设置 256mb。

因此,您有一个在直接内存池中创建的 2GB 缓冲区。默认情况下,直接内存的最大大小等于 - 在您的情况下,24GB。我可以看到一个场景,您的直接内存池会膨胀到24GB(从尚未清除的旧缓冲区),使您的应用程序的总大小至少!-Xmx20 + 2 + 24 + 24 = 70GB

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

该怎么办

  1. 选择一个理智的.我不知道选择2GB背后的原因,但如果您确定自己在小型WAL段上遇到问题,我建议使用默认的64mb或256mb。walSegmentSize

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

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


答案 2

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

原来:case class Value(@(QueryTextField@field) theta: String)

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


推荐