Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8225508

Compiler OOM Error with Type Inference Hierarchy

    Details

    • Subcomponent:
    • CPU:
      generic
    • OS:
      generic

      Description

      ADDITIONAL SYSTEM INFORMATION :
      Attempted with JDKs 1.8.0_191, 1.8.0_212, 11.0.3, and 12.01, on both OS X High Sierra and Ubuntu 18.04.

      A DESCRIPTION OF THE PROBLEM :
      When calling Arrays.asList() without type arguments and on arguments within a nested type hierarchy, the compiler uses exponentially more memory as interfaces are added; with the source code provided, adding 4 interfaces to the classes compiles with a max heap size of 128m, 5 interfaces fails as 2g but succeeds with 3g, and 6 interfaces still fails at 12g.

      This may have something to do with Arrays.asList() using parametrically polymorphic variadic arguments; on the initial case, List.of() presented the same issues with 11 arguments (tripping the varargs case), but not with 10 when it switched back to an overload using 10 explicit arguments. However, the current test case shows Arrays.asList() and List.of() behaving identically.

      Perhaps related to https://bugs.openjdk.java.net/browse/JDK-8055984.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Following the provided source code:
      1) Create a class (ConsStruct) with subclasses (Empty and Cons) extending it
      2) Create interfaces which are intended to carry the class implementing them as type witnesses (interfaces I through M)
      3) Create extensions of the main class (ConsStruct) defined in terms of each other (in the source code, as tuples: A4/5/6, B4/5/6, and C4/5/6, with C being defined in terms of B defined in terms of A)
      4) Have the extensions implement the interfaces (5 for failure with heap size of 2g, 6 for failure beyond that)
      5) Call Arrays.asList() (or List.of()) without additional type arguments on instances of the extensions

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The code would correctly infer and compile with a reasonably-growing amount of heap space
      ACTUAL -
      The code does not compile, due to lack of heap space

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

      public class InterfaceOverload {
          private interface I<X, IWitness extends I<?, IWitness>> { }
          private interface J<X, JWitness extends J<?, JWitness>> { }
          private interface K<X, KWitness extends K<?, KWitness>> { }
          private interface L<X, LWitness extends L<?, LWitness>> { }
          private interface M<X, MWitness extends M<?, MWitness>> { }
          private interface N<X, MWitness extends N<?, MWitness>> { }

          private static class ConsStruct {
              private static class Empty extends ConsStruct { }
              private static class Cons<X, Y extends ConsStruct> extends ConsStruct { }
          }

          // Cons-ing types: (C5, (B5, (A5, Empty)))
          // Implementing I through M (5 interfaces)
          private static class A5<X> extends ConsStruct.Cons<X, ConsStruct.Empty> implements
                  I<X, A5<?>>, J<X, A5<?>>, K<X, A5<?>>, L<X, A5<?>>, M<X, A5<?>> { }
          private static class B5<X, Y> extends ConsStruct.Cons<X, A5<Y>> implements
                  I<Y, B5<X, ?>>, J<Y, B5<X, ?>>, K<Y, B5<X, ?>>, L<Y, B5<X, ?>>, M<Y, B5<X, ?>> { }
          private static class C5<X, Y, Z> extends ConsStruct.Cons<X, B5<Y, Z>> implements
                  I<Z, C5<X, Y, ?>>, J<Z, C5<X, Y, ?>>, K<Z, C5<X, Y, ?>>, L<Z, C5<X, Y, ?>>, M<Z, C5<X, Y, ?>> { }

          // Cons-ing types: (C4, (B4, (A4, Empty)))
          // Implementing I through L (4 interfaces)
          private static class A4<X> extends ConsStruct.Cons<X, ConsStruct.Empty> implements
                  I<X, A4<?>>, J<X, A4<?>>, K<X, A4<?>>, L<X, A4<?>> { }
          private static class B4<X, Y> extends ConsStruct.Cons<X, A4<Y>> implements
                  I<Y, B4<X, ?>>, J<Y, B4<X, ?>>, K<Y, B4<X, ?>>, L<Y, B4<X, ?>> { }
          private static class C4<X, Y, Z> extends ConsStruct.Cons<X, B4<Y, Z>> implements
                  I<Z, C4<X, Y, ?>>, J<Z, C4<X, Y, ?>>, K<Z, C4<X, Y, ?>>, L<Z, C4<X, Y, ?>> { }

          // Cons-ing types: (C6, (B6, (A6, Empty)))
          // Implementing I through N (6 interfaces)
          private static class A6<X> extends ConsStruct.Cons<X, ConsStruct.Empty> implements
                  I<X, A6<?>>, J<X, A6<?>>, K<X, A6<?>>, L<X, A6<?>>, M<X, A6<?>>, N<X, A6<?>> { }
          private static class B6<X, Y> extends ConsStruct.Cons<X, A6<Y>> implements
                  I<Y, B6<X, ?>>, J<Y, B6<X, ?>>, K<Y, B6<X, ?>>, L<Y, B6<X, ?>>, M<Y, B6<X, ?>>, N<Y, B6<X, Y>> { }
          private static class C6<X, Y, Z> extends ConsStruct.Cons<X, B6<Y, Z>> implements
                  I<Z, C6<X, Y, ?>>, J<Z, C6<X, Y, ?>>, K<Z, C6<X, Y, ?>>, L<Z, C6<X, Y, ?>>, M<Z, C6<X, Y, ?>>, N<Z, C6<X, Y, ?>> { }

          public static void main(String[] args) {
              A5<Boolean> foo1 = new A5<>();
              B5<Boolean, Integer> foo2 = new B5<>();
              C5<Boolean, Integer, String> foo3 = new C5<>();

              A4<Boolean> bar1 = new A4<>();
              B4<Boolean, Integer> bar2 = new B4<>();
              C4<Boolean, Integer, String> bar3 = new C4<>();

              A6<Boolean> baz1 = new A6<>();
              B6<Boolean, Integer> baz2 = new B6<>();
              C6<Boolean, Integer, String> baz3 = new C6<>();

              // Compiles - type hints
              java.util.List<ConsStruct.Cons<Boolean, ? extends ConsStruct>> asList5WithHints =
                      Arrays.<ConsStruct.Cons<Boolean, ? extends ConsStruct>>asList(foo1, foo2, foo3);
              java.util.List<ConsStruct.Cons<Boolean, ? extends ConsStruct>> asList6WithHints =
                      Arrays.<ConsStruct.Cons<Boolean, ? extends ConsStruct>>asList(baz1, baz2, baz3);

              // Compiles at 128m - only 4 interfaces
              java.util.List<ConsStruct.Cons<Boolean, ? extends ConsStruct>> asList4WithoutHints =
                      Arrays.asList(bar1, bar2, bar3);

              // Runs out of memory at -Xmx2g, but not -Xmx3g
              java.util.List<ConsStruct.Cons<Boolean, ? extends ConsStruct>> asList5WithoutHints =
                      Arrays.asList(foo1, foo2, foo3);

              // Runs out of memory at -Xmx3g and above
              java.util.List<ConsStruct.Cons<Boolean, ? extends ConsStruct>> asList6WithoutHints =
                      Arrays.asList(baz1, baz2, baz3);
          }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Provide type arguments

      FREQUENCY : always


        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                vromero Vicente Arturo Romero Zaldivar
                Reporter:
                webbuggrp Webbug Group
              • Votes:
                0 Vote for this issue
                Watchers:
                2 Start watching this issue

                Dates

                • Created:
                  Updated: