JDK-8189234 : Unexpected StackOverflowError when using 'limit' on infinite list generated with recursive 'flatMap' call
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.stream
  • Affected Version: 9
  • Priority: P4
  • Status: Resolved
  • Resolution: Duplicate
  • OS: windows_7
  • CPU: x86_64
  • Submitted: 2017-10-11
  • Updated: 2018-01-24
  • Resolved: 2018-01-24
Related Reports
Duplicate :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "9"
Java(TM) SE Runtime Environment (build 9+181)
Java HotSpot(TM) 64-Bit Server VM (build 9+181, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]

A DESCRIPTION OF THE PROBLEM :
The 'limit' method is specified to be a short-circuiting operation, but when used on an infinite Stream generated through use of 'flatMap' the limit is inconsistently applied, leading to a StackOverflowError.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile and run the attached code.  The problem occurs with primitive streams (IntStream and company) or the generic object Stream.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The code should print 
4
3
2
1
0
and then exit cleanly
ACTUAL -
The code prints
4
3
2
1
0
and then throws a StackOverflowError

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "main" java.lang.StackOverflowError
	at java.base/java.util.stream.Streams$StreamBuilderImpl.forEachRemaining(Streams.java:411)
	at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:734)
	at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:591)
	at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:272)
	at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
	at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:312)
	at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735)
	at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:591)
	at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:272)
	at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
	at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:312)
	at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735)
	at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:591)
	at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:272)

(etc.)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.util.IntStream;

public class StreamBugTest {
    public static void main(String[] args) {
        getChildren(5).limit(5).forEach(System.out::println);
    }

    private static IntStream getChildren(int i) {
        return IntStream.rangeClosed(i - 1, i + 1).flatMap(c -> IntStream.concat(IntStream.of(c), StreamBugTest .getChildren(c)));
    }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Must re-write code


Comments
The StackOverflowError is reproducible with the attached test case.
12-10-2017