我们正在将 Drools Fusion 应用程序从 6.4.0 升级到 8.44.2。该应用程序仅管理 1 个在流模式下长时间运行的 KieSession (config.setOption(EventProcessingOption.STREAM))。事件被插入到在 fireUntilHalt() 中运行的会话中。
在 6.4.0 中,同一会话可能会运行数天或数周。当我们仅仅依靠事件的自动驱逐(根据我们的配置,在 24 小时后)时,我们经历过事件可能会极端爆发并导致内存不足问题。因此,我们添加了一些额外的逻辑,在事件插入之前检查会话中已存在的事件不超过 N 个。如果发生这种情况,我们将选择 20% 最旧的事件并手动从会话中删除相关事实(使用如下代码,ep 是入口点,Event 是我们的应用程序域事件类)。
for (Event e : toDelete) {
FactHandle fh = ep.getFactHandle(e);
if (fh != null) {
ep.delete(fh);
}
}
相同的代码现在可以在 8.44.2 上运行,但据我们所知,内存消耗正在缓慢但稳定地增加。手动删除逻辑似乎仍然有效,我们看到相关的日志条目。但会话在某些时候会走向 OOM。内存直方图看起来像这样
在该测试设置中,我们始终检查事件数量是否保持在 11'000 以下。如果达到此级别,则将从会话中删除最旧的 2000 个。尽管如此,活跃事件的数量似乎仍在增加。大多数内存似乎都保存在 TruthMaintenanceSystemRuleTerminalNodeLeftTuple 类中。
我必须承认,我不知道这个类的用途是什么,也不知道为什么它的实例显然被“永远”保留。知道这里会发生什么吗?
因为您正在处理入口点以获取事实句柄并从中删除,您能否确认您是从正确的入口点删除的?您真的是指 drl 中的入口点(因为会话也实现了入口点)吗?为什么在第 3 行有一个条件来处理在工作内存中找不到对象的情况,这是预期的(业务逻辑的一部分)还是您只是隐藏了错误的删除逻辑(从错误的入口点删除)?
1 for (Event e : toDelete) {
2 FactHandle fh = ep.getFactHandle(e);
3 if (fh != null) {
4 ep.delete(fh);
5 }
6 }
您能否尝试其他删除方式,而不是依赖 drools 工作内存之外的代码来跟踪对象(并存储对它们的引用)?
global AtomicLong maxCount
rule "maxCount"
salience -1
when
$new: CountedModel()
not CountedModel(ordinal > $new.ordinal)
$old: CountedModel()
not CountedModel(ordinal < $old.ordinal)
then
if ($new.ordinal - $old.ordinal >= maxCount.get()) {
delete($old);
}
end