JDK-8351990 : JVM implementation of strict field initialization
  • Type: Sub-task
  • Component: hotspot
  • Sub-Component: runtime
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2025-03-13
  • Updated: 2025-09-23
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdUnresolved
Related Reports
Blocks :  
Relates :  
Description
This task summarizes the JVM changes introduced by the Strict Field Initialization in the JVM JEP. All features are activated by the `--enable-preview` launcher option.

Full JVMS changes to come...


### ACC_STRICT_INIT flag

Any field may be marked `ACC_STRICT_INIT` (`0x0800`, used in legacy classes to indicate `strictfp` semantics for methods, but no longer meaningful) to indicate that it is strictly initialized. The flag may be used in combination with any other flag.


### Instance initialization states

The initialization state of the current class instance is tracked by the verifier using one of the following:

- *earlyLarval(unsetFields)*, where *unsetFields* is a list of strict instance fields of the current class that need to be set

- *unrestricted*, a general-purpose symbol covering the *late larval* and *initialized* states, and also used for static methods

- *erroneous*, indicating that initialization has failed, and preventing any exception handlers (a placeholder symbol similar, in some ways, to *afterGoto*)

These initialization state values replace the 'flags' verification parameter. (Note that, with the exception of the list of unset fields, this is just a presentational change, expressing the combination of 'flags' and an available 'uninitializedThis'.)

The initial state of an <init> method is *earlyLarval(...)*, with unset fields including all strict instance fields declared by the class. The initial state of any other method is *unrestricted*.

The 'frameIsAssignable' predicate only allows transitions between identical states, or between different *earlyLarval* states where the targeted frame has a superset of unset fields.


### Verification of instructions

'putfield':

- In an *earlyLarval(...)* state, when operating on 'uninitializedThis' and referencing the current class, the outgoing initialization state removes the named field, if present, from the list of uninitialized fields (the exceptional state is unchanged)

- For other 'putfield' instructions, reject an attempt to write to a 'strict' 'final' instance field referenced & declared in the current class

'invokespecial':

- When invoking an <init> method on an uninitializedThis, the incoming initialization state must be *earlyLarval(...)*. The outgoing initialization state is *unrestricted* and the exceptional initialization state is *erroneous*.

- When invoking an <init> method on an uninitializedThis, if the method is referenced in the superclass, the list of unset fields must be empty.

'return':

- The 'return' instruction is only allowed in the *unrestricted* state.


### StackMapTable changes

The StackMapTable is enhanced to allow the following entry, which is not a *frame*, but affects the interpretation of subsequent frames:

assert_early_unset_fields {
    u1 frame_type = ASSERT_EARLY_UNSET_FIELDS; /* 246 */
    u2 number_of_unset_fields;
    u2 unset_fields[number_of_unset_fields];
}

Each unset_fields entry must reference a NameAndType constant with a field descriptor, and (probably?) that NameAndType must match a strict instance field declared in the current class, or the StackMapTable is malformed.

The stack frames of the StackMapTable are interpreted as follows:

- If the frame has a local variable of type 'uninitializedThis', the initialization state is *earlyLarval(...)*, with unset fields given by the closest preceding 'assert_early_unset_fields' entry (or, if none, the initial set of unset fields)

- If the frame does not have a local variable of type 'uninitializedThis', the initialization state is *unrestricted*

(Note that the StackMapTable does not support expressing a handler for the *erroneous* state of a failed invokespecial. This is intentional, consistent with current behavior.)


### Class initialization states

The *larval* class initialization state (referred to in JVMS as "being initialized") currently keeps track of the thread performing initialization; this JEP adds information to track whether each static field has been initialized, and whether each static final field has been read.

When the ConstantValue attribute of a field is applied, the class initialization state is updated to reflect that the field has been initialized.


### Static field checks

'putstatic' (and similar reflective operations), when the resolved field's declaring class is in a *larval* state:

- If the field is strict & static & final, the state must indicate that the field has not yet been read, or an exception (TBD) thrown

- If the field is static & final, the state must indicate that the field has not yet been read, or a diagnostic is generated (see below)

- If the field is static, the state is updated to indicate that the field has been initialized

'getstatic' (and similar reflective operations), when the resolved field's declaring class is in a *larval* state::

- If the field is strict & static, the state must indicate that the field has been initialized, or an exception (TBD) is thrown

- If the field is static, the state must indicate that the field has been initialized, or a diagnostic is generated (see below)

- If the field is static & final, the state is updated to indicate that the field has been read

After a '<clinit>' method is invoked (or, if none is declared, after it *would have* been invoked), the class initialization state must indicate that every strict static field of the class has been initialized, or an exception (TBD) is thrown.


### Static field diagnostics

A command-line flag (TBD) controls how diagnostics for non-strict fields generated during static field reads and writes are presented to users:

- By default, the event is ignored

- Under one configuration, the diagnostic is logged to the console and reported to JFR

- Under another configuration, the diagnostic results in a fatal error