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

implement strict static fields (proposed JVM feature)

XMLWordPrintable

      This bug tracks a prototype of strict statics, which are fields with both modifiers ACC_STRICT and ACC_STATIC (and maybe others like ACC_FINAL).

      The semantics are such that a first write to such a field must occur before the first read, and before the containing class exits from its <clinit>. (If there is no <clinit>, then it is in error, since they cannot have been initialized.) Writes include putstatic and uses of reflection and method handle APIs, which therefore must respect the new rules. JNI must also respect it, but happily JNI expects to operate after the relevant classes are already initialized.

      Discussion and background:

      Design work in Valhalla motivates new JVM features for ensuring that object fields and class fields (non-statics and statics) are initialized in a timely way, and do not allow any party to observe their uninitialized states. Such fields are called "strictly initialized" or simply "strict".

      At least in principle, strict fields can be either final or non-final, static or non-static. However, the implementation details for statics and non-statics differ greatly, however similar they may seem to a user of the Java language.

      For both non-statics and statics, a strict final is inherently more trustable than a non-strict final, simply because there is no chance of observing its uninitialized (null) state, not even in the presence of initialization races and circularities of various sorts. After initialization hazards are past, the strict final is safe to constant-fold.

      A strict static is made strict by dynamically tracking reads and writes (getstatic and putstatic) during its early lifetime, from class loading through class initialization. Crucially, any strict static must be written before either of two events occur: Before the static field is read (getstatic), and before the class is declared fully initialized (exit from <clinit>). A dynamic exception is thrown if either rule is violated, causing the class to become permanently non-initialized.

      After class initialization, a strict static, final or non-final, has about the same properties as a regular static (with other properties being the same). If it is final, it is a reliable constant, which never changes. If not a final, it may change in the future. It may be marked "stable" as well.

      These properties align with strict non-statics, which are tracked for initialization (and bad reads) up to the object constructor call to its super constructor. The tracking is done statically (in the verifier) instead of dynamically (in the VM runtime). Again, after that point, a strict non-static (final or not) looks like any kind of non-static field, except that it is more trusted for constant folding.

      The crucial inherent difference between strict statics and strict non-statics is that strict non-statics are 100% unobservable during the "larval" period of their containing object. (This "larval" period is from object allocation to first superclas constructor invocation, the "invokespecial" of super.) Now, classes (with their statics) also have a "larval" period, but the access is less limited. Most threads are blocked from accessing a class's state until it is initialized, but a single thread runs <clinit>, and that thread has full rights to inspect static state (using getfield) directly in the <clinit> method, or indirectly through methods and field references from other classes. This means that a dynamic mechanism must be used to track reads and writes. This mechanism only applies during execution of <clinit>, and in just one thread. Therefore, it cannot have races, and its cost is limited to the "larval" period of the class being initialized. Afterwards there is no tracking and no extra overhead for strict fields.

      So strict statics and non-statics must have differering enforcement mechanisms: statics are checked non-statically, and non-statics are checked statically — oddly enough.

      These different enforcement mechanisms lead to differences in bytecode semantics, notably that larval non-statics cannot be read (getfield) but larval statics may be read (getstatic), as long as the read happens in the critical section of <clinit>, and in the same thread.

      Still, it seems desirable to remove unnecessary differences. It is simplest and most effective for example, to allow multiple writes for larval statics and non-statics.

      Allowing reads of larval non-statics is a possibility, but it would require more complex verifier rules, which are likely to be expensive and buggy. Disallowing reads of larval statics would be a compatibility problem.

      (Disallowing reads of *uninitalized* larval statics is an incompatibility, but that is surely a good change. In any case it is necessary for value class fields in Valhalla, and for null-restricted fields of T! type.)

            Unassigned Unassigned
            jrose John Rose
            Votes:
            0 Vote for this issue
            Watchers:
            7 Start watching this issue

              Created:
              Updated: