[GR-39406] Add new class initialization strategy that allows all classes to be used at image build time. by graalvmbot · Pull Request #4684 · oracle/graal (original) (raw)

This PR adds a less strict class initialization mode that allows all classes to be used at image build time. It can be enabled with -H:+UseNewExperimentalClassInitialization.

The new approach is functional, i.e., it can be used for experiments to see if the new approach is useful and to see if there are compatibility problems.. It is not optimized yet, i.e., far more classes will be initialized at image run time so peak performance will be lower for now. The new approach will only be enabled by default once all optimizations of the current approach are also available for the new approach.

Current approach

Problems with the current approach

New less restricted approach

Compatibility impact

Examples

To make the examples more readable, each class name has a suffix:

We use the pseudo-field $$initialized to refer to the class initialization status of a class.

Example 1

class A_InitAtBuildTime {
  static int a = 42;
}

class B_InitAtRunTime {
  static int b = 123;

  static {
    A_InitAtBuildTime.a = A_InitAtBuildTime.a + 1;
  }
}

Assume that B_InitAtRunTime is not used by a Feature and therefore does not get initialized by the image builder VM. A_InitAtBuildTime gets initialized by the image builder VM because of its manual designation, regardless of how it is used at image build time. So in the image builder, the static field values are

A_InitAtBuildTime.$$initialized = true
A_InitAtBuildTime.a = 42

B_InitAtRunTime.$$initialized = false
B_InitAtRunTime.b = 0

The same values are written out into the image heap.

After the class B_InitAtRunTime gets initialized at run time, the static fields have the following values:

A_InitAtBuildTime.$$initialized = true
A_InitAtBuildTime.a = 43

B_InitAtRunTime.$$initialized = true
B_InitAtRunTime.b = 123

Example 2

class A_InitAtBuildTime {
  static int a = 42;
}

class B_InitAtRunTime {
  static int b = 123;

  static {
    A_InitAtBuildTime.a = A_InitAtBuildTime.a + 1;
  }
}

Assume that B_InitAtRunTime is used by a Feature and therefore gets initialized by the image builder VM. So in the image builder, the static field values are

A_InitAtBuildTime.$$initialized = true
A_InitAtBuildTime.a = 43

B_InitAtRunTime.$$initialized = true
B_InitAtRunTime.b = 123

In the written out image, the following static field values are in the image heap:

A_InitAtBuildTime.$$initialized = true
A_InitAtBuildTime.a = 43

B_InitAtRunTime.$$initialized = false
B_InitAtRunTime.b = 0

After the class B_InitAtRunTime gets initialized at run time, the static fields have the following values:

A_InitAtBuildTime.$$initialized = true
A_InitAtBuildTime.a = 44

B_InitAtRunTime.$$initialized = true
B_InitAtRunTime.b = 123

The usage of B_InitAtRunTime at image build time has a bad side effect on A_InitAtBuildTime, whose field got incremented twice. But by explicitly marking A_InitAtBuildTime as initialize-at-build-time, the user has acknowledged that such side effects are understood.

Our recommendation for users should be: Only mark a class for initialization at build time if it does not have 1) any mutable static state (including any mutable data structures reachable from static final fields), and 2) a class initializer that accesses any mutable state of another class.

Example 3

class A_InitAtBuildTime {
  static Object a;
}

class MyFeature implements Feature {
  void beforeAnalysis(...) {
    A_InitAtBuildTime.a = new A_InitAtBuildTime();
  }
}

The feature code runs before static analysis and initializes the static field. So in the image builder, the static field values are

A_InitAtBuildTime.$$initialized = true
A_InitAtBuildTime.a = <A_InitAtBuildTime instance>

This is a correct use of build time initialization: classes that store information computed a build time must be marked as "initialize at build time".

Example 4

class A_InitAtBuildTime {
  static Object a;
}

class B_InitAtRunTime {
}

class MyFeature implements Feature {
  void beforeAnalysis(...) {
    A_InitAtBuildTime.a = new B_InitAtRunTime();
  }
}

The image build fails: The image heap must not only contain instances of classes that are marked as "initialize at build time".