Apache Ignite SqlFieldsQuery内存泄漏

时间:2017-01-24 08:28:55

标签: java memory-leaks ignite

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

UPD:

我在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”的一个实例中累积。

possible memory leak

1 个答案:

答案 0 :(得分:1)

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

(36 * 2(字节/字符)+(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本身和许多其他事情的开销。

我猜这就是Out of Memory异常的原因。

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