Question

I know there are many topics and resources about this, but I'm wondering about a very specific question (and it might take a very long time to check all sources for a definite answer).

I know that JVM/Dalvik guarantees that by the time you access a static field of a class (except for final static primitive values), the static fields of the class are already initialized. Is the opposite true as well? If I never access a class at all (e.g. because the switch-case code in another static method never reaches a certain branch), is it guaranteed that the VM does not initialize statics of this class?

Assume I have a class such as this:

public class Boo {
      public static int[] anything = new int[] { 2,3,4 };
      private static int[] something = new int[] { 5,6,7 }; // this may be much bigger as well

      public static final int[] getAndClear() {
           int[] st = something;
           something = null;
           return st;
      }
}

My application is a very special one (not typical in some aspects), and it may hold hundreds of classes such as Boo (generated by a code generator), where something may be an array of varying element count (so it may contain very many elements as well sometimes).

Depending on application input, many of these pregenerated classes might never get accessed. I do not want that a lot of int[] objects get initialized unnecessarily, eating up much memory.

Was it helpful?

Solution

I know that JVM/Dalvik guarantees that by the time you access a static field of a class (except for final static primitive values), the static fields of the class are already initialized.

This is mostly true but is not the case for certain static fields because of constant inlining. In

class A {
  public static final String FOO = "foo";

  static { System.out.println("loaded A"); }
}

public class B {
  public static void main(String... argv) {
    System.out.println("Got " + A.FOO);
  }
}

the JVM will print "Got foo" but will not print "loaded A". In fact, B will run even if A.class is not on the class-path, though at least one of A.java or A.class must be available when compiling B.java.


Is the opposite true as well? If I never access a class at all (e.g. because the switch-case code in another static method never reaches a certain branch), is it guaranteed that the VM does not initialize statics of this class?

Yes. The JLS lays out the exact conditions under which class loading and initialization occurs, so JVM implementations have no freedom to eagerly load or initialize classes.

12.4.1 is the chapter of interest.

12.4.1. When Initialization Occurs

A class or interface type T will be initialized immediately before the first occurrence of any one of the following:

  1. T is a class and an instance of T is created.
  2. T is a class and a static method declared by T is invoked.
  3. A static field declared by T is assigned.
  4. A static field declared by T is used and the field is not a constant variable (§4.12.4).
  5. T is a top level class (§7.6), and an assert statement (§14.10) lexically nested within T (§8.1.3) is executed.

The "immediately before" verbiage forbids any eagerness, and mandates what happens when two classes both try to perform one of the actions above -- there is a lock associated with a loaded but uninitialized class, and both wait until the thread that first acquires that lock performs the initialization.

The "and the field is not a constant variable (§4.12.4)" verbiage is the exception to the rule that I illustrated by class B using A.FOO above.


public static final int[] getAndClear() { ... }

should probably be synchronized because otherwise two threads might both get the same array instead of one receiving null. The class loader lock that I mention above does not protect getAndClear.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top