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

ArrayInitializers and OutOfMemoryError



    • Subcomponent:
    • Resolved In Build:
    • CPU:
    • OS:
    • Verification:


      Name: boT120536 Date: 01/23/2001

      java version "1.3.0"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0-C)
      Java HotSpot(TM) Client VM (build 1.3.0-C, mixed mode)

      The JLS does not mention that evaluating an ArrayInitializer can exit abruptly
      with an OutOfMemoryError, or give guidelines as to the timing of the OutOfMemory
      with respect to initializer expression evaluation.

      Section 10.6 on Arrays does not mention that evaluation of the array initializer
      can throw an OutOfMemoryError. For that matter, it does not even cover the case
      of an expression inside the initializer completing abruptly, just that they are
      executed left to right. I am assuming, based on analogous situations discussed
      in chapter 15, that if an initializer expression completes abruptly, all
      initializers expressions to the right are not evaluated, and the enclosing
      initializer or overall assignment completes abruptly for the same reason.

      Section 14.4.4 states that a local variable declaration can complete abruptly if
      evaluating the initializer expression completes abruptly, but does not elaborate
      on ArrayInitializers. Section 8.3.2 talks about initializing non-local
      variables, but does not even consider that class or instance initializations can
      complete abruptly. Likewise, section 15.10.1 does not mention the possibility
      during an array creation expression of an initializer creating abruptly.
      Fortunately, at least sections 12.4.2 and 12.5 do mention what happens when
      evaluation of a class or instance variable initializer exits abruptly.

      Therefore, the following code has undefined behavior, regarding whether an
      OutOfMemoryError would occur:
      class arraymem {
        public static void main(String[] args) {
          Object o = null;
          while (true) try {
            Object[] oa = {o};
            o = oa;
          } catch (OutOfMemoryError e) {
            o = null; // allow for recovery
            System.out.println("Caught: " + e);

      One compiler that I tried rejected the catch block as unreachable, since nothing
      in the JLS said that a variable declaration or simple assignment statement could
      cause an OutOfMemoryError. Javac 1.3 compiles this, but has other known bugs
      regarding reachability analysis of catch blocks. Execution was another story: on
      an HP machine, JDK 1.1.8, some variations of this program just quit executing
      once the garbage collector could allocate no more space, while others correctly
      printed the error message (depending on whether I was using a static int as a
      progress indicator of array nesting depth). On Windows98, JDK 1.3, java just
      hung. A -verbosegc run showed it paused at "[Full GC" for at least ten minutes,
      when I finally gave up. On Linux, JDK 1.3, HotSpot dies with a stack overflow.

      It looks like the correct version of the rules for array initializer evaluation
      (which should be included in 10.6, and possibly elsewhere) are as follows:
      When evaluating an array initializer, these two steps are taken:

      First, space is allocated for every element of the overall initializer. This
      involves a sequence of allocations of one-dimensional arrays, each with length
      determined by the number of elements in the initializer, and component type
      based on the nesting level. Depth-first vs. breadth-first allocation should not
      matter. If all the elements of a particular array initializer share identical
      dimensions, the compiler can choose to optimize the allocation into a single
      allocation of a multi-dimensional array. If any one of these allocations fails,
      the overall array initializer completes abruptly with an OutOfMemoryError. The
      elements of these new arrays start with their default value.

      Next, the VariableInitializers are processed from left to right. If the nth
      element is an expression, the expression is evaluated. If it is an array
      initializer, this step is recursively repeated. In either case, if the variable
      initializer completes abruptly, the array initialization completes abruptly for
      the same reason. Otherwise, the result of the expression or the array returned
      by the array initializer is assigned to the n-1st position within the array. If
      all the elements complete normally, the array initializer completes normally,
      with the value of the newly initialized array.

      Therefore, this example:
      void foo() {
        int a=0, b=0, c=0, d=0, e=0, f=0;
        try {
          int[][] ia = {{a = 1, b = 2}, {c = 3, d = 4}, {e = 5, f = 6}};
        } catch (OutOfMemoryError e) {
          // free something, to make sure the print will work
          System.out.println(a + " " + b + " " + c + " " + d);

      can only have one outcome if an OutOfMemoryError occurs, which will be:
      0 0 0 0 0 0

      This is a slight change from the current state of 10.6, which states that even
      the array initializers are evaluated from left to right, intermixed with other
      expressions. (Javac 1.3 does this mixed operation, as can be seen by looking at
      a decompiled class file.) The reason the existing specification is wrong is
      that it leaves multiple points for an OutOfMemoryError to occur, meaning the
      above code could have three possible outputs:
      0 0 0 0 0 0 // OutOfMemoryError on outer array, or first inner
      1 2 0 0 0 0 // failure on second inner
      1 2 3 4 0 0 // failure on third

      The current policy goes against the policy of deterministic execution wherever
      possible, as well as being dissimilar from other constructs. For example, if
      the expression:
      Object o = new int[a=1][b=2];
      fails with an OutOfMemoryError, it is guaranteed that both a and b were
      assigned, even if the VM could have determined after a=1 that there would not be
      enough memory regardless of the evaluation of the second bracketed expression.

      On a side note, the JLS should also require compile-time errors if an array
      initialization has more than 255 dimensions, per the limitation mentioned in the
      JVMS 4.10, and the documentation for java.lang.reflect.Array.
      (Review ID: 113542)




            abuckley Alex Buckley
            bonealsunw Bret O'neal (Inactive)
            0 Vote for this issue
            0 Start watching this issue