问题描述
正在对 infinispan/jgroups 之上的集群解决方案进行一些内部测试,并注意到由于过期收割机上的引用,而在集群中有 1 个以上的节点时,过期的条目永远不会符合 GC 的条件启用过期/禁用驱逐。 由于某些系统问题,正在使用以下版本:
- JDK 1.8
- Infinispan 9.4.20
- jgroups 4.0.21
在我的示例中,我使用了一个简单的 Java 主场景,放置了特定数量的数据,期望它们在特定时间段后过期。过期确实发生了,因为它可以在访问过期条目和相应的事件侦听器(如果已配置)时确认,看起来它永远不会从可用内存中删除,即使在显式 GC 或同时接近 OOM 错误。
所以问题是:
这真的是默认行为吗,或者我缺少集群复制/到期/序列化的关键配置?
示例:
缓存管理器:
return new DefaultCacheManager("infinispan.xml");
infinispan.xml :
<jgroups>
<stack-file name="udp" path="jgroups.xml" />
</jgroups>
<cache-container default-cache="default">
<transport stack="udp" node-name="${nodeName}" />
<replicated-cache name="myLeakyCache" mode="SYNC">
<expiration interval="30000" lifespan="3000" max-idle="-1"/>
</replicated-cache>
</cache-container>
.....
<UDP
mcast_addr="${jgroups.udp.mcast_addr:x.x.x.x}"
mcast_port="${jgroups.udp.mcast_port:46655}"
bind_addr="${jgroups.bind.addr:y.y.y.y}"
tos="8"
ucast_recv_buf_size="200k"
ucast_send_buf_size="200k"
mcast_recv_buf_size="200k"
mcast_send_buf_size="200k"
max_bundle_size="64000"
ip_ttl="${jgroups.udp.ip_ttl:2}"
enable_diagnostics="false"
bundler_type="old"
thread_naming_pattern="pl"
thread_pool.enabled="true"
thread_pool.max_threads="30"
/>
虚拟缓存条目:
public class CacheMemoryLeak implements Serializable {
private static final long serialVersionUID = 1L;
Date date = new Date();
}
来自“服务”的示例用法:
Cache<String,Object> cache = cacheManager.getCache("myLeakyCache");
cache.put(key,new CacheMemoryLeak());
一些信息/试用:
- 当集群中只有一个节点或重启它们时 引用依次被清除。
- 启用 Max-idle 显示相同的行为(有意义的到期 收割者是一样的)
- 启用驱逐并不能解决问题,只会保留 “过期”引用计数在最大限制之间。如果这是 达到非常快,实时条目正在发生随机驱逐 还有(默认删除策略)!!
- 如果我将 Cache 条目更改为原生字符串,则 infinispan.MortalCacheEntries 正在从堆空间中移除 在下一个 GC 循环中,与自定义对象相比,在过期并被过期收割者标记时!!
- 仅在一个节点中启用到期收割者并没有解决问题 问题,并且可能会破坏故障转移机制。
- 已升级到 infinispan 10.1.8 Final,但面临同样的问题。
解决方法
因为似乎没有其他人有同样的问题或使用原始对象作为缓存条目,因此没有注意到这个问题。 复制并幸运地追查到根本原因后,出现以下几点:
- 始终为即将通过复制/同步缓存传输的自定义对象实现 Serializable /
hashCode
/equals
。 - 永远不要放置原始数组,因为
hashcode
/equals
不会被有效地计算 - - 不要在复制缓存上使用移除策略启用驱逐,因为达到最大限制时,条目将随机移除 - 基于 TinyLFU - 而不是基于过期的计时器,并且永远不会从 JVM 堆中移除.