“同步”会影响流的内存占用吗?

Sim*_*mon 5 java stream out-of-memory java-stream

我们有一个奇怪的案例OutOfMemory(堆)。给定这种方法

private void processRemainingIds(final ITransaction tx) {
    remainingIds.stream()//
        .map(this::getInternalMessage)//
        .filter(this::isMessageNeedsProcessing)//
        .forEach(msg -> registerMessageAsMissing(msg, tx));
}
Run Code Online (Sandbox Code Playgroud)

如果remainingIds足够大,则此方法会相当稳定地填充堆。

  • getInternalMessage 将加载“正常”大小的数据模型结构(即,没有斑点/小块等,只有几十个字符串和数字)
  • registerMessageAsMissing 内部调用同步方法(可能是相关的)
  • 使getInternalMessage“同步”完全改变了内存行为,堆大小不再增加

我希望上面的实现会创建很多内部消息,检查并在需要时对其进行处理,但随后丢弃每个对象并偶尔运行GC。但这不是我们所看到的,相反,我们得到了

越来越多的堆

OOM的标准问题,即 我熟悉的是“某些东西在固定住您的对象”。但是为什么要进行getInternalMessage同步更改呢?

Sim*_*mon 1

事实证明,更改为“同步”只是转移注意力,它既不是原因,也不是解决方案。

原因是 EclipseLink 的 UnitOfWork,它默认保存对每个加载实体的引用。当getInternalMessage()加载许多对象时,堆慢慢填满。解决方案是使用ReferenceMode.FORCE_WEAK,它只创建对已加载实体的弱引用,从而允许对它们进行 GC。

我推测,使getInternalMessage() 同步改变了执行,使得对象加载速度变慢,也许给GC更多的时间。这可能会导致同一时间段内内存增长变慢,但我们后来发现,使用同步我们也遇到了 OOM。