JDK-8246289 : Set state to 'linked' when an archived boot class is restored at runtime
  • Type: CSR
  • Component: hotspot
  • Sub-Component: runtime
  • Priority: P4
  • Status: Closed
  • Resolution: Withdrawn
  • Fix Versions: tbd
  • Submitted: 2020-06-02
  • Updated: 2021-07-20
  • Resolved: 2021-07-20
Related Reports
CSR :  
Description
Summary
-------

Set class state to 'linked' when an archived boot class (loaded by the NULL class loader) is restored at runtime.

Problem
-------

The Java Virtual Machine Specification [1]  ��5.4 states: ���Linking a class or interface involves verifying and preparing that class or interface, its direct superclass, its direct superinterfaces, and its element type (if it is an array type), if necessary���. 

��5.5 states: ���Prior to initialization, a class or interface must be linked, that is, verified, prepared, and optionally resolved.��� [1] ��5.5

At archive dumping time, the VM loads boot classes using the NULL loader. The loaded classes are fully linked at dump time. The class states are reset to ���allocated��� before writing into the archive file (.jsa). After an archived boot class is loaded at runtime, it needs to go through normal class linking process again like any other dynamically loaded classes. Avoiding that duplicated work can improve runtime CPU utilization.

The Java Class Pre-resolution and Pre-initialization [2] proposal (proposed for the project Leyden) seeks a JVM spec compliant solution for selective class pre-initialization. The proposal also seeks an optimal approach with the least CPU and memory overhead. In order to achieve that, an archived boot class needs to be placed in 'linked' state early.

Solution
--------

When linking a class, the VM first links the super types (all super classes and super interfaces) of the current class. It then verifies and rewrites the bytecode for the current class, checks loader constraints, links methods, initializes the itable and vtable, and sets the current class to 'linked' state. 

As described in above section, an archived boot class is fully linked (all linking work described above is done) at CDS dump time and the class state is reset to 'allocated' before the archive file is written. When loading an archived class at runtime, the VM makes sure all the super types in the class hierarchy for the current class are loaded first. If not, the archived class is not used and the VM then dynamically loads the requested class from the JAR file. That ensures the runtime class hierarchy is the same as dump time for any loaded archived boot class. Therefore, the class linking results achieved at CDS dump time remain valid at runtime.

An archived class is restored when it is 'loaded' at runtime. At the end of the restoration, all methods are linked and no other additional linking work is necessary for an archived boot class. The class is effectively in 'linked' state from that point. As a result, the VM can safely set the class state to 'linked' at the end of restoration. That avoids the work for re-iterating the super types in the class hierarchy.

Setting an archived class in 'linked' state during restoration enables the class to be placed in 'fully_initialized' state early for future optimizations.

During the normal class linking process, the VM posts ClassPrepare event for any JVMTI agent who registers the related callback. For an archived boot class, the VM is changed to post the ClassPrepare event immediately after the class is restored and set to 'linked' state. That does not affect dynamically loaded classes. The VM continues to post the ClassPrepare events for dynamically loaded classes at normal linking time, which usually happens as part of the class initialization.

JVM spec [1] ��5.4 allows eager or lazy linking (please see below). JVMTI agents should not rely on the exact timing or order of linking related events, particular the ClassPrepare event in this case. 

"This specification allows an implementation flexibility as to when linking activities (and, because of recursion, loading) take place, provided that all of the following properties are maintained:

��� A class or interface is completely loaded before it is linked.
��� A class or interface is completely verified and prepared before it is initialized.
��� Errors detected during linkage are thrown at a point in the program where some
action is taken by the program that might, directly or indirectly, require linkage
to the class or interface involved in the error.
��� A symbolic reference to a dynamically-computed constant is not resolved until
either (i) an ldc, ldc_w, or ldc2_w instruction that refers to it is executed, or (ii)
a bootstrap method that refers to it as a static argument is invoked.
��� A symbolic reference to a dynamically-computed call site is not resolved until a
bootstrap method that refers to it as a static argument is invoked."

Specification
-------------

Specific change to archived boot class linking process:
```
At the end of InstanceKlass::restore_unshareable_info, if the
current class' loader is the NULL class loader, the archived
class' '_init_state' is set to be 'linked'.

If JvmtiExport::should_post_class_prepare() is true, the VM
calls JvmtiExport::should_post_class_prepare() to notify JVMTI
agents in SystemDictionary::define_instance_class().
```
webrev: http://cr.openjdk.java.net/~jiangli/8232222/webrev.02/

[1] https://docs.oracle.com/javase/specs/jvms/se14/jvms14.pdf

[2] http://cr.openjdk.java.net/~jiangli/Leyden/Java%20Class%20Pre-resolution%20and%20Pre-initialization%20(OpenJDK).pdf
Comments
Moved to Withdrawn as suggested. Thanks.
20-07-2021

This CSR has been in the Provisional state for quite some time. If there is no longer the interesting in pursuing the issue, [~jiangli] please move the CSR to Withdrawn.
20-07-2021

Thanks for the clarification! The anticipated modifications are modest based on the feedback so far, will keep the state in provisional state.
01-07-2020

[~jiangli], from a procedural point of view, it is fine for the CSR to state in provisional state. If there are large modifications tot the request in the future it can be moved made to Draft (than Proposed) to go through the full two-phase CSR process from the start. If there are more modest changes, this CSR can be moved to Finalized after one or more reviewers is added. HTH
27-06-2020

Joe, thanks a lot for the comments and suggestion. I meant to respond much earlier, but was distracted. Sorry for the delay. For the 'link' state change itself, how about documenting/clarifying it in the Class Data Sharing section (for example, https://docs.oracle.com/en/java/javase/14/vm/class-data-sharing.html#GUID-EC975B2E-B4AB-45B4-B91F-51C3A264D0CE)? Coleen pointed out the link state change might have conflicts with the JEP for New Invoke Bindings. I'll work with Erik to seek a potential solution. Is it okay to keep the CSR in PROVISIONAL state before that's resolved? I can move it back to draft state if that's better. The single Preserve annotation type with a boolean field sounds good to me. It's indeed much cleaner than using two annotations. Thanks for the suggestion! For the long run, Mark also suggested using keyword earlier in the discussion mailing list.
25-06-2020

The current behavior for ClassPrepare event posting is: + The ClassPrepare event for class X is posted immediately after X's init state has been set to "linked" + The ClassPrepare event for class X is always posted after the ClassPrepare events for all of X's supertypes have been posted and handled by the JVMTI event handlers.
22-06-2020

This CSR fails to mention one very important change in behavior: with the proposal that "the VM calls JvmtiExport::should_post_class_prepare() to notify JVMTI agents in SystemDictionary::define_instance_class()" it's possible for a child class's ClassPrepare event to be posted before that of the parent class. This is contrary to the current behavior. Also, SystemDictionary::define_instance_class() is part of the HotSpot implementation and it's behavior is unspecified. We should not specify JVMTI agent behavior based on HotSpot internals.
22-06-2020

Sorry for the belated review; moving to Provisional. How would this change be documented, a release note? Before the request is Finalized for the second phase of CSR review, please have one or more other engineers review the request. For the referenced pdf, instead of a pair of @jdk.internal.vm.annotation.{Preserve���, DontPreserve} annotation types, I suggest considering a single Preserve annotation type with a boolean field that defaults to true. Besides only needing a single type, this design avoids having to define what happens in the case both annotations are present.
22-06-2020