Skip to content

Commit

Permalink
incorporate all review comments
Browse files Browse the repository at this point in the history
  • Loading branch information
John Rose committed Dec 11, 2024
1 parent ff94e0f commit 273aa2e
Showing 1 changed file with 168 additions and 84 deletions.
252 changes: 168 additions & 84 deletions src/java.base/share/classes/jdk/internal/vm/annotation/Stable.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,23 @@

/**
* A field may be annotated as "stable" to indicate that it is a
* <em>stable variable</em>, expected to change value at most once.
* The first value stored into the field (assuming it is not a
* duplicate of the the field's default initial value), allows the VM
* to assume that no more significant changes will occur. This in
* turn enables the VM to optimize the stable variable, treating uses
* of it as constant values. This behavior is a useful building block
* <em>stable variable</em>, expected to change its value just once.
* While the field contains its initial default (null or zero) value,
* the VM treats it as an ordinary mutable variable. When the
* first value is stored into the field (assuming it is not a
* duplicate of the the field's default initial value), the VM may
* assume that no more significant changes will occur. This in
* turn enables the VM to optimize uses of the stable variable, treating
* them as constant values. This behavior is a useful building block
* for lazy evaluation or memoization of results. In rare and subtle
* use cases, stable fields may also assume on multiple values over
* use cases, stable variables may also assume multiple values over
* time, with effects to be described below.
* <p>
* (Warning: the {@code @Stable} annotation is intended for use in the
* JDK implemention, and with the HotSpot VM, to support optimization
* of classes and algorithms defined by the JDK. It is unavailable
* outside the JDK.)
* <p>
* For example, declaring two stable fields of type {@code int} and
* {@code String} creates a pair of stable variables, initialized to
* zero and the null reference (respectively). Storing a non-zero
Expand All @@ -60,56 +67,100 @@
* programmer should store non-default values into stable variables,
* if the consequent optimization is desired.
* <p>
* As a special case, if a stable field is declared as an array type
* with one dimension, both the array as a whole, and its eventual
* components, are treated as independent stable variables. When a
* reference to an array of length <i>N</i> is stored to the field,
* then the array object itself is taken to be a constant, as with any
* stable field. But then all <i>N</i> of the array components are
* <em>also</em> treated as independent stable variables. Such a
* stable array may contain any type, reference or primitive.
* A stable variable may be assigned its final value inside a class or
* object initializer, but in general (with lazy data structures)
* stable variables are assigned much later. Depending on the value
* stored and what races are possible, safe publication may require
* special handling with a {@code VarHandle} atomic method.
* (See below.)
* <p>
* As an extended behavior, if a stable field is declared as an array
* type with one dimension, both that array as a whole, and its
* eventual components, are treated as independent stable variables.
* When a reference to an array of length <i>N</i> is stored to the
* field, then the array object itself is taken to be a constant, as
* with any stable field. But then all <i>N</i> of the array
* components are <em>also</em> treated as independent stable
* variables. Such a stable array may contain any type, reference or
* primitive. Such an array may be also marked {@code final}, and
* initialized eagerly in the class or object initializer method.
* Whether any (or all) of its components are also initialized eagerly
* is up to the application.
* <p>
* More generally, if a stable field is declared as an array type with
* <em>D</em> dimensions, then all the non-null components of the
* array, and of any sub-arrays up to a nesting depth less than
* <em>D</em>, are treated as stable variables. Thus, a stable field
* declared as an array potentially defines a tree (of fixed depth
* <em>D</em>) containing many stable variables, with each such stable
* variable is independently considered for optimization. In this
* variable being independently considered for optimization. In this
* way, and depending on program execution, a single {@code Stable}
* annotation can potentially create many independent stable
* variables. Since the top-level array reference is always stable,
* it is in general a bad idea to resize the array, even while keeping
* all existing components unchanged. (This could be relaxed in the
* future, to allow expansion of stable arrays, if there were a use
* case that could deal correctly with races.)
* case that could deal correctly with races. But it would require
* careful treatment by the compiler, to avoid folding the wrong
* version of an array. Anyway, there are other options, such as
* tree structures, for organizing the expansion of bundles of stable
* variables.)
* <p>
* An array is never intrinsically stable. There is no change made to
* an array as it is assigned to a stable variable of array type.
* This is true even though after such an assignment, the compiler may
* observe that array and treat its components as stable variables.
* If the array is aliased to some other variable, uses via that
* variable will not be treated as stable. (Such aliasing is not
* recommended!) Also, storing an array into a stable variable will
* not make that array's components into stable variables, unless the
* variable into which it is stored is statically typed as an array,
* in the declaration of the stable field which refers to that array,
* directly or indirectly. Thus, in the following example, the only
* constant-foldable string is {@code "S"}. All subarrays are
* constant.
*
* <pre>{@code
* @Stable Object[] ARRAY = {
* "S", // string "S" is stable
* new String[] { "X", "Y" }, // array is stable, not elements
* null // null is default reference, not yet stable
* };
* @Stable Object[][] MATRIX = {
* { "S", "S" }, // constant value
* { new String[] { "X", "Y" } }, // both arrays stable, not elements
* { null, "S" }, // array is stable, but not null
* null // not yet stable (could be stable array later)
* };
* }</pre>
* <p>
* As very simple example, a stable boolean variable (i.e., stable
* field or stable boolean array element) might be constant-folded,
* but only after it is set to {@code true}. Even this simple
* optimization is sometimes useful for responding to a permanent
* one-shot state change, in such a way that the compiler can remove
* dead code associated with the initial state. As with any stable
* variable, it is in general a bad idea to reset such a variable to
* its default (i.e., {@code false}), since compiled code might have
* captured the {@code true} value as a constant, and as long as that
* compiled code is in use, the reset value will go undetected.
* <p>
* As very simple example, a boolean variable is constant-folded only
* when it is set to {@code true}. Even this simple behavior is
* sometimes useful for recording a permanent one-shot state change,
* in such a way that the compiler can remove dead code associated
* with the initial state. It is in general a bad idea to reset
* such a variable to {@code false}, since compiled code might have
* "frozen" the {@code true} value, and will never detect the reset
* value.
* Stability of a variable and the constant folding of its default
* value (null or zero) are mutually exclusive, as explained above.
* So {@code final} field might be both null and also a constant, but
* not a stable field. If an application requires constant folding of
* the default value, it can add an extra indirection to "box" the
* default value, such as {@code Integer.valueOf(0)} or a lambda like
* {@code ()->null}. Such a refactoring should always be possible,
* since stable variables should (obviously) never be part of public
* APIs.
* <p>
* Fields which are declared {@code final} may also be annotated as stable.
* Since final fields already behave as stable values, such an annotation
* Since final fields already behave as stable variables, such an annotation
* conveys no additional information regarding change of the field's value, but
* still conveys information regarding change of additional component values if
* it conveys information regarding changes to additional component variables if
* the type of the field is an array type (as described above).
* <p>
* There are special times early in application startup when constant
* folding of stable variables is disabled. Specifically, the VM does
* not observe the effects of any {@code @Stable} annotation until
* after an AOT cache assembly phase (if any) has run to completion.
* Therefore, during these early times, a stable variable can be
* changed in any way, just like a regular mutable variable (field or
* array component). If a field is annotated {@code @Stable} but is
* treated in this way during AOT cache assembly, that fact must be
* clearly stated as a warning on the field declaration. If there is
* no such warning, maintainers can ignore this edge case.
* <p>
* In order to assist refactoring between {@code final} and
* {@code @Stable} field declarations, the Java Memory Model
* <em>freeze</em> operation is applied to both kinds of fields, when
Expand All @@ -124,15 +175,15 @@
* (either field or array component) will appear without races to any
* user of the class or object that has been initialized.
* <p>
* There is no such freeze operation applied to stable field stores in
* There is no such JMM freeze operation applied to stable field stores in
* any other context. This implies that a constructor may choose to
* initialize a stable variable, rather than "leaving it for later",
* and that initial will be safely published, as if the field were
* initialize a stable variable, rather than "leaving it for later".
* Such an initial value will be safely published, as if the field were
* {@code final}. The stored value may (or may not) contain
* additional stable variables, not yet initialized. Note that if a
* stable variable is written outside of the code of a constructor (or
* class initializer), then data races are possible, just the same as
* if there were no {@code @Stable} annotation, and the field was a
* if there were no {@code @Stable} annotation, and the field were a
* regular mutable field. In fact, the usual case for lazily
* evaluated data structures is to assign to stable variables much
* later than the enclosing data structure is created. This means
Expand All @@ -141,28 +192,18 @@
* <p>
* Therefore, most code which reads stable variables should not assume
* that the value has been set, and should dynamically test for a null
* (or zero) value. Code which cannot prove proper ordering of
* initialization may use stable variables without performing the null
* (or zero) test. Code which omits the null (or zero) test should be
* (or zero) value. Code which cannot prove a previous initialization
* must perform a null (or zero) test on a value loaded
* from a stable variable. Code which omits the null (or zero) test should be
* documented as to why the initialization order is reliable. In
* general, some sort of critical section for initialization should be
* pointed out as provably preceding all uses of the (unchecked)
* stable variable. Examples of such a critical section would be a
* constructor body which directly writes a final or stable variable,
* or the AOT cache assembly phase as a whole.
* <p>
* The HotSpot VM relies on this annotation to promote a non-null (or
* non-zero) stable variable use to a constant, thereby enabling superior
* optimizations of code depending on the value (such as constant folding).
* More specifically, the HotSpot VM will process non-null stable fields (final
* or otherwise) in a similar manner to static final fields with respect to
* promoting the field's value to a constant. Thus, placing aside the
* differences for null/non-null values and arrays, a final stable field is
* treated as if it is really final from both the Java language and the HotSpot
* VM.
* <p>
* After constant folding, the compiler can make use of may aspects of
* the object: Its dynamic type, its length (if it is an array), and
* documented, as provably preceding all uses of the (unchecked)
* stable variable, or else reasons should be given why races are
* benign, or some other proof given that races are either excluded or
* benign. See below for further discussion.
* <p>
* After constant folding, the compiler can make use of many aspects of
* the object: its dynamic type, its length (if it is an array), and
* the values of its fields (if they are themselves constants, either
* final or stable). It is in general a bad idea to reset such
* variables to any other value, since compiled code might have folded
Expand All @@ -172,19 +213,26 @@
* and treats annotated fields (and any affected arrays) as regular
* mutable variables. Thus, a field annotated as {@code @Stable} may
* be given a series of values, by explicit assignment, by reflection,
* or by some other means. If the HotSpot compiler observes one of
* these values, and constant-folds it in the setting of some
* particular compilation task, then in some contexts (execution of
* fully optimized code) the field will appear to have one
* "historical" value, while in others (less optimized contexts) the
* field will appear to have a more recent value. (And it is no good
* to try to "reset" a stable value by storing its default again,
* because there is currently no way to find and deoptimize any and
* all affected compiled code.) Race conditions would make this even
* or by some other means. If the HotSpot compiler constant-folds a
* stable variable, then in some contexts (execution of fully
* optimized code) the variable will appear to have one "historical"
* value, observed, captured, and used within the compiled code to the
* exclusion of any other possible values. Meanwhile, in other less
* optimized contexts, the stable variable will appear to have a more
* recent value. Race conditions, if allowed, will make this even
* more complex, since with races there is no definable "most recent"
* value.
* value across all threads. The compiler can observe any racing
* value, as it runs concurrently to the application, in its own
* thread.
* <p>
* It is no good to try to "reset" a stable variable by storing its
* default again, because there is (currently) no way to find and
* deoptimize any and all affected compiled code. If you need the
* bookkeeping, try {@code SwitchPoint} or {@code MutableCallSite},
* which both are able to reset compiled code that has captured an
* intermediate state.
* <p>
* Note also each compiliation task makes its own decisions about
* Note also each compilation task makes its own decisions about
* whether to observe stable variable values, and how aggressively to
* constant-fold them. And a method that uses a stable variable might
* be inlined by many different compilation tasks. The net result of
Expand All @@ -202,17 +250,53 @@
* appropriately so that unwanted values are not observable by
* compiled code.
* <p>
* The {@code @Stable} annotation is intended for use in the JDK
* implemention, and with the HotSpot VM, to support optimization of
* classes and algorithms defined by the JDK. Any class which uses
* this annotation is responsible for constraining assignments in such
* a way as not to violate API contracts of the class. Such
* constraints can be arranged using explicit atomic access
* (sychronization, CAS, etc.), or by concealing the effects of
* multiple assignments in some API-dependent way, or by providing
* some other internal proof of correctness (accounting for any
* possible racing API access), or by some appropriate disclaimer in
* the API about undefined behavior.
* Any class which uses this annotation is responsible for
* constraining assignments in such a way as not to violate API
* contracts of the class. (If the chosen technique is unusual in
* some way, it should be documented in a comment on the field.) Such
* constraints can be arranged in a variety of ways:
* <ul><li> using the {@code VarHandle} API to perform an explicit
* atomic operation such as {@code compareAndExchange},
* {@code setRelease}, {@code releaseFence}, or the like.
* </li><li> using regular variable access under explicit sychronization
* </li><li> using some other kind of critical section to avoid races
* which could affect compiled code
* </li><li> allowing multiple assignments under benign races, but
* only of some separately uniquified value
* </li><li> allowing multiple assignments under benign races, but
* only of semantically equivalent values, perhaps permitting
* occasional duplication of cached values
* </li><li> concealing the effects of multiple assignments in some
* other API-dependent way
* </li><li> providing some other internal proof of correctness, while
* accounting for all possible racing API accesses
* </li><li> making some appropriate disclaimer in the API about
* undefined behavior
* </li></ul>
* <p>
* There may be special times when constant folding of stable
* variables is disabled. Such times would amount to a critical
* section locking out the compiler from reading stable variables.
* During such a critical section, an uninitialized stable variable
* can be changed in any way, just like a regular mutable variable
* (field or array component). It can even be reset to its default.
* Specifically, this may happen during certain AOT operations. If a
* stable variable can be updated multiple times during such a
* critical section, that fact must be clearly stated as a comment on
* the field declaration. (In the future, there may be explicit
* AOT-related annotations to convey this use case.) If there is no
* such warning, maintainers can safely disregard the possibility of
* an AOT critical section, since the author of the stable variable is
* relying on one of the other techniques listed above.
* <p>
* It is possible to imagine markings for foldable methods or fields,
* which can constant-fold a wider variety of states and values. This
* annotation does not readily extend to such things, for the simple
* reason that extra VM bookkeeping would be required to record a
* wider variety of candidate states for constant folding. Such
* higher-level mechanisms may be created in the future. The present
* low-level annotation is designed as a potential building block to
* manage their bookkeeping.
*
* @implNote
* This annotation only takes effect for fields of classes loaded by the boot
Expand Down

0 comments on commit 273aa2e

Please sign in to comment.