JDK-8156610 : Stack overflow error with Consumer interface
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.stream
  • Affected Version: 8,9
  • Priority: P4
  • Status: Closed
  • Resolution: Won't Fix
  • OS: generic
  • CPU: generic
  • Submitted: 2016-05-09
  • Updated: 2016-05-10
  • Resolved: 2016-05-10
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "9-ea"
Java(TM) SE Runtime Environment (build 9-ea+116)
Java HotSpot(TM) 64-Bit Server VM (build 9-ea+116, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 10.0.10586]

A DESCRIPTION OF THE PROBLEM :
The java.util.function.Consumer interface has a utility "andThen" method for chaining Consumers. The implementation calls each Consumer recursively, and a long chain leads to a stack overflow. The other functional interfaces might have similar problems.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Construct a long Consumer chain, by calling the andThen method.


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Expected to see a long printout of dots.
ACTUAL -
Exception in thread "main" java.lang.StackOverflowError
        at java.util.function.Consumer.lambda$andThen$0(java.base@9-ea/Consumer.java:65)
        at java.util.function.Consumer.lambda$andThen$0(java.base@9-ea/Consumer.java:65)
        at java.util.function.Consumer.lambda$andThen$0(java.base@9-ea/Consumer.java:65)
...

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
public class ConsumerTest implements Consumer<Object> {
    public static void main(String[] args) throws Exception {
        Consumer<Object> c = new ConsumerTest();
        for (int i=0; i<10000; i++) {
            c = c.andThen(new ConsumerTest());
        }
        c.accept(null);
    }

    @Override
    public void accept(Object obj) {
        System.out.print('.');
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Define a custom class for chaining Consumers.


Comments
There is not much we can do about this right now. We are not going to add alternative methods that somehow use the heap (in a thread safe manner) to store dependent chains. We need support for tail-call elimination: https://blogs.oracle.com/jrose/entry/tail_calls_in_the_vm
10-05-2016

Attached test case ran fine upto 8900 iterations, after that it ran only on increasing the stack size (java -Xss1g JI9037244) . On modification of the test case, so that the chaining and consumption both happen at each iteration, it gives expected results without any need of increasing the stack size.
10-05-2016