我们有一个奇怪的案例OutOfMemory(堆)。给定这种方法
private void processRemainingIds(final ITransaction tx) {
remainingIds.stream()//
.map(this::getInternalMessage)//
.filter(this::isMessageNeedsProcessing)//
.forEach(msg -> registerMessageAsMissing(msg, tx));
}
如果remainingIds足够大,则此方法会相当稳定地填充堆。
getInternalMessage
将加载“正常”大小的数据模型结构(即,没有blob/clobs等,只有几十个字符串和数字)registerMessageAsMissing
在内部调用一个同步方法(也许是相关的)getInternalMessage
'synchronized'完全改变了内存行为,堆大小不再增加 我希望上面的实现会创建很多内部消息,检查并在需要时对其进行处理,但随后丢弃每个对象并偶尔运行GC。但这不是我们所看到的,相反,我们得到了
OOM的标准问题,即我熟悉的是“某些东西在固定住您的对象”。但是,为什么使
getInternalMessage
同步会发生什么变化呢? 最佳答案
事实证明,更改为“同步”是一个红鲱鱼,既不是原因,也不是解决方案。
原因是EclipseLink的UnitOfWork,默认情况下它拥有对每个已加载实体的引用。当getInternalMessage()
加载许多对象时,堆慢慢填充了。解决方案是使用ReferenceMode.FORCE_WEAK
,它仅创建对已加载实体的弱引用,从而允许对其进行GC处理。
我推测使getInternalMessage()
同步会更改执行,以使对象加载速度变慢,也许会给GC多一点时间。在同一时间段内,这可能会导致内存倾斜速度变慢,但是后来我们发现,在同步的情况下,我们也遇到了OOM。