[英]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.