繁体   English   中英

Apache Ignite SqlFieldsQuery 内存泄漏

[英]Apache Ignite SqlFieldsQuery memory leak

似乎 SqlFieldsQuery 会导致内存泄漏。 我正在尝试测试 Ignite.destroyCache 方法以确保在此方法之后清除所有缓存资源。 但似乎当我们使用 SqlFieldsQuery 时,一些缓存资源并没有被释放。

我写了一个小测试来证明这一点。

import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.Ignition;
import org.apache.ignite.binary.BinaryObject;
import org.apache.ignite.binary.BinaryObjectBuilder;
import org.apache.ignite.cache.QueryEntity;
import org.apache.ignite.cache.query.SqlFieldsQuery;
import org.apache.ignite.cluster.ClusterMetrics;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.junit.Test;

import java.util.Collections;
import java.util.UUID;
import java.util.stream.IntStream;

public class CacheDestroyTest {

    private static final String BO_TEST = "bo_test";

    @Test
    public void shouldDestroyCacheAndFreeMemory() throws Exception {
        IgniteConfiguration igniteCfg = new IgniteConfiguration();
        igniteCfg.setGridName("CacheDestroyTest");
        try (Ignite ignite = Ignition.start(igniteCfg)) {
            for (int i = 0; i < 100; i++) {
                CacheConfiguration<Integer, BinaryObject> cfg = new CacheConfiguration<>();
                cfg.setQueryEntities(Collections.singletonList(queryEntity()));
                cfg.setName("DestroyCacheAndFreeMemory_test" + i);

                IgniteCache<Integer, BinaryObject> cache = ignite.getOrCreateCache(cfg);
                IntStream.range(0, 200_000).parallel().forEach(id -> cache.put(id,
                        buildBinary(ignite.binary().builder(BO_TEST))));
                cache.withKeepBinary().query(new SqlFieldsQuery("select * from " + BO_TEST + " limit 100")).getAll();
                ignite.destroyCache(cache.getName());
                System.out.println("Iteration " + i + " done");
                ClusterMetrics metrics = ignite.cluster().metrics();
                System.out.println("HeapMemoryTotal " + metrics.getHeapMemoryTotal() / 1024 / 1024);
                System.out.println("HeapMemoryUsed " + metrics.getHeapMemoryUsed() / 1024 / 1024);
            }
        }
    }

    private QueryEntity queryEntity() {
        QueryEntity entity = new QueryEntity();
        entity.setKeyType(Integer.class.getName());
        entity.setValueType(BO_TEST);
        for (int i = 0; i < 20; i++) {
            entity.addQueryField("data_" + i, String.class.getName(), null);
        }
        return entity;
    }

    private BinaryObject buildBinary(BinaryObjectBuilder builder) {
        for (int i = 0; i < 20; i++) {
            builder.setField("data_" + i, UUID.randomUUID().toString());
        }
        return builder.build();
    }

    public static void main(String[] args) throws Exception {
        new CacheDestroyTest().shouldDestroyCacheAndFreeMemory();
    }

}

我在这个测试中在 2 次迭代后得到了 java.lang.OutOfMemoryError(使用 -Xmx512m 运行这个测试)。

Iteration 0 done
HeapMemoryTotal 498
HeapMemoryUsed 399
Iteration 1 done
HeapMemoryTotal 497
HeapMemoryUsed 484

没有线

cache.withKeepBinary().query(new SqlFieldsQuery("select * from " + BO_TEST + " limit 100")).getAll();

测试工作正常

Iteration 0 done
HeapMemoryTotal 498
HeapMemoryUsed 277
Iteration 1 done
HeapMemoryTotal 460
HeapMemoryUsed 328
Iteration 2 done
HeapMemoryTotal 455
HeapMemoryUsed 323
Iteration 3 done
HeapMemoryTotal 485
HeapMemoryUsed 316
Iteration 4 done
HeapMemoryTotal 504
HeapMemoryUsed 309
Iteration 5 done
HeapMemoryTotal 504
HeapMemoryUsed 300

我做错了什么还是错误? 我的 Apache Ignite 版本是 1.8

更新:

我在 5 次迭代后进行了堆转储。 疑似 MAT 问题:“sun.misc.Launcher$AppClassLoader @ 0x6c18385f0”加载的“org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing”的一个实例占用了 1,083,398,360 (80.77%) 个字节。 内存在“sun.misc.Launcher$AppClassLoader @ 0x6c18385f0”加载的“org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing$StatementCache”的一个实例中累积。

可能的内存泄漏

我将首先进入您正在创建的二进制对象。 生成的 RandomUUID 长度为 36 个字符(至少在我的机器中)。 这使得字符串的长度

(36*2(bytes/char)+(8+4+4)(java 中字符串的开销)) = 88 字节。

此外,data_(int) 字段名称需要额外的 28 个字节。

因此,一个二进制对象需要 116 字节的数据。

因此,20 个这样的二进制对象需要 2320 个字节。

现在,让我们考虑单个缓存条目:值(20 个二进制对象)占用 2320 个字节,键(整数)占用 4 个字节。

这为我们每个缓存条目提供了 2324 字节。

因此,对于 2,00,000(这是我从 IntStream.range(0, 200_000) 推断出的)缓存条目,所需的内存为 464 MB。

现在,这还没有考虑 Ignite 本身的开销和许多其他事情。

我想这就是内存不足异常的原因。

我假设 Ignite Grid 在一台机器上运行。

暂无
暂无

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

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