Java 19并行流中skip()为何导致8GB内存下出现OOM?
为什么添加skip(2)会导致并行流内存溢出(OOM)?
现象描述
在Java 19环境、8GB内存配置下,以下带skip(2)的并行IntStream代码会触发OOM:
IntStream .iterate(0, i -> i + 1) .skip(2) .limit(10_000_000) .filter(i -> checkSum(i) <= 20) .parallel() .count();
但移除skip(2)后,代码可以正常运行不会出现OOM:
IntStream .iterate(0, i -> i + 1) //.skip(2) .limit(10_000_000) .filter(i -> checkSum(i) <= 20) .parallel() .count();
其中checkSum方法实现如下:
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; }
核心原因分析
- IntStream.iterate生成的是无序无限流:和
IntStream.range()这种有明确范围的有序流不同,iterate生成的流没有固定边界,并行处理时无法直接拆分为均匀的子任务段。 - skip(2)破坏了limit的任务分配优化:
无skip时,并行流处理iterate+limit组合时,内部会尝试给每个线程分配大致等量的连续数值任务,避免任务过度拆分。
但添加skip(2)后,流结构变为skip -> limit,为了保证准确跳过前2个元素,每个线程在处理limit内的元素前,必须先处理并丢弃前2个元素。这导致原本的批量任务分配逻辑失效,只能生成大量细小的任务。 - 任务队列堆积占用内存:
并行流依赖ForkJoinPool处理任务,skip+limit的组合会让ForkJoinPool不断拆分出大量小任务。当任务生成速度远快于线程处理速度时,这些待处理任务会在队列中大量堆积,占用的内存持续增长,最终触发OOM。 - checkSum方法加速了内存溢出:
checkSum是相对耗时的计算操作,线程处理单个任务的时间变长,进一步加剧了任务队列的堆积速度,更快耗尽内存。
内容的提问来源于stack exchange,提问作者mmirwaldt
相关产品推荐
相关产品推荐

