如果我这样做,在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()。但是,这并没有回答我这个问题。我想了解这里的问题是什么。
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();