提问者:小点点

为什么Java 19中这个并行流表达式中的skip()即使有8GB也会引起OOM?


如果我这样做,在Java 19中即使8GB也会出现OOM:

IntStream
.iterate(0, i -> i + 1)
.skip(2)
.limit(10_000_000)
.filter(i -> checkSum(i) <= 20)
.parallel()
.count();

但是,如果我省略跳过(2),我不会得到任何OOM:

IntStream
.iterate(0, i -> i + 1)
//.skip(2)
.limit(10_000_000)
.filter(i -> checkSum(i) <= 20)
.parallel()
.count();

校验和(…)在哪里

public static long checkSum(long n) {
    long result = 0;
    long remaining = n;
    while (0 < remaining) {
        long remainder = remaining % 10;
        result += remainder;
        remaining = (remaining - remainder) / 10;
    }
    return result;
}

为什么Java 19中这个并行流表达式中的skip()即使有8GB也会引起OOM?

我知道我应该使用range(…)而不是iterate()limited(),不管有没有skip()。但是,这并没有回答我这个问题。我想了解这里的问题是什么。


共1个答案

匿名用户

skip()-是一个有状态的操作,它保证省略流的n第一个元素(相对于遇到顺序,如果流是有序的)。

它在顺序管道中会很便宜,但如果对流进行排序,则在并行运行时可能会很昂贵。文档对此发出警告,并建议尽可能放宽约束顺序。

API注释:

虽然skip()在顺序流管道上通常是一种便宜的操作,但在有序并行管道上可能非常昂贵,特别是对于n的大值,因为skip(n)不仅可以跳过任何n个元素,还可以跳过相遇顺序中的前n个元素。如果您的情况的语义学允许,使用无序流源(例如生成(供应商))或删除BaseStream.unordered()的排序约束可能会导致并行管道中的skip()显着加速。如果需要与遇到顺序保持一致,并且您在并行管道中使用skip()遇到性能或内存利用率不佳,则使用BaseStream.sequential()切换到顺序执行可能会提高性能。

添加重点

以下无序 Stream 将毫无问题地运行(流执行的结果可能不一致,因为在无序流线程中,可以自由丢弃任何 n 个元素,而不是首先丢弃 n):

IntStream
    .iterate(0, i -> i + 1)
    .unordered()
    .skip(2)
    .limit(10_000_000)
    .filter(i -> checkSum(i) <= 20)
    .parallel()
    .count();