JDK-8245858 : Enhance Java heap object (subgraph) archiving for more general support of selective class/static field pre-initialization
  • Type: Sub-task
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 15
  • Priority: P4
  • Status: Closed
  • Resolution: Won't Fix
  • Submitted: 2020-05-26
  • Updated: 2024-01-10
  • Resolved: 2024-01-10
Related Reports
Blocks :  
Description
Design doc: http://cr.openjdk.java.net/~jiangli/Leyden/Java%20Class%20Pre-resolution%20and%20Pre-initialization%20(OpenJDK).pdf

Class Initializing Phase
-------------------------------
At CDS dump time, some of the loaded classes may be implicitly initialized, as a result of executing Java code to load classes from the classlist. The VM needs to explicitly initialize the rest of the classes that can be pre-initialized. The operation is performed after loading all classes from the classlist and before copying & relocating class metadata to archive spaces.

Explicit class initialization is done via calling InstanceKlass::initialize() and follows the procedure described in JVM specification [1] section §5.5.

Subgraph Checking Phase
------------------------------------
Java heap object archiving process is done after class metadata copying and relocation step. All directly and indirectly reachable Java heap objects from a reference type static field form a complete subgraph. A static field should not be archived if any of the following types of objects are found in its subgraph:

Non-mirror java.lang.Class objects
ClassLoader objects
java.security.ProtectionDomain objects
java.lang.Thread objects
Runnable objects
java.io.File objects
TBD

Before initialized static field values are archived, the VM iterates all classes that can be pre-initialized to walk all reachable objects from every reference type static field within a class using depth-first search algorithms and makes sure unsuitable values (please see above) are not archived. During the search, if a non-preservable object is found, the state is populated upwards to all the objects (nodes) that directly or indirectly reference the non-preservable object. As a result, it avoids traversing a subgraph again if the same object that contains a non-preservable subgraph is encountered again during the process.

A class is excluded for preserving if any of the reference type static fields is found not archivable/prevserable. 

Static Field Values Preserving Phase
-------------------------------------------------
The static field value preserving is built on top of the existing algorithm that supports selective static field archiving, JDK-8202035. During the process, the VM starts from a root object, which is the value of a reference type static field (referenced from a mirror object) and is also the entry point of a subgraph formed by all directly and indirectly reachable objects from the root. The VM follows references and walks all objects within the subgraph using depth-first search algorithms, and copies objects to the archive heap region. Pointers (references) within the archived objects are updated as part of the process.

This design enhances the current subgraph archiving technique by allowing mirrors (java.lang.Class instances that represent loaded classes on the Java side). During the subgraph walking and archiving process, if a mirror object is encountered the VM archives the mirror but stops following references from it. Mirrors can be safely included in an archived subgraph because they are singleton objects representing loaded classes within the system. Dump time resets mirror’s instance fields to default values (see default value descriptions for primitive types and reference type in JVM spec [1] §2.3 & §2.4 sections), which guarantees no non-preservable objects are included in the subgraph. Archived mirrors are restored and materialized during runtime class loading time for the corresponding archived classes. The mechanism built in the static field archiving ensures all dependent classes (the class types of the objects included within a subgraph) are initialized before an archived static field value is materialized at runtime. That guarantees mirrors within any of the materialized subgraph are fully initialized and usable when being accessed at runtime. Supporting mirror objects enables more classes for pre-initialization. Such classes include java.lang.Integer, java.lang.Long, etc.

Non-mirror java.lang.Class objects cannot be preserved in the current scope as they contain references to java.lang.ClassLoader object, java.security.ProtectionDomain object, etc, which contain data that’s specific to a runtime execution environment and should not be preserved. Further research is needed to seek a solution that can support archiving ClassLoader objects.

The existing static field subgraph archiving resets all local static fields within a mirror object and stores the archived field values (reference types only) in a separate per-class record. With this design, all archived static field values including primitive types and reference types from a class are preserved within the class’ archived mirror object. Runtime does not need to store the values back to the fields. The separate per-class record is still needed to store the dependent classes of the current class.

Archived Class State Flags
------------------------------------
Two archived class states are introduced in this design to facilitate further runtime optimizations during archived class restoration and initialization:

* is_pre_initialized
* is_preserved

The is_pre_initialized Flag
====================

An archived class is set with the is_pre_initialized flag, iff both of the following conditions are true for the class:

- The static fields are primitive types or special reference types. Currently those special reference types are limited to j.l.Object and j.l.String types, which are initialized before other classes. 
- The current class is an interface, or the direct superclass is j.l.Class or an archived class with is_pre_initialized flag set.

At runtime when an archived class with is_pre_initialized flag is loaded, the class can be set to fully_initialized state, which avoids the overhead of traversing super types in the normal initialization process. 

The is_preserved Flag
=================
The is_preserved flag is used to indicate that an archive class contains preserved static field values.
Runtime Process for Archived Classes with Preserved Static Fields
At runtime when an archived class is loaded and restored, if its is_pre_initialized flag is set, the class is set to fully_initialized state when restore is done. No additional initialization work is needed. That is the most optimized case.

If the class does not have the is_pre_initialized flag set, it is set to linked state when restore is completed. When the class initialization is triggered during Java code execution (see JVM specification [1] §5.5, the VM initializes all its super types first. Then retrieves the archived class record. All dependent classes listed in the record are initialized. If the is_preserved flag is set, no additional work is needed to populate the field values. Otherwise, the values for all static fields are retrieved from the record and stored back into the mirror object. The entry objects (for a subgraph) of the reference type static fields are materialized. The <clinit> method is not executed and the class is set to fully_initialized state.

[1] The Java® Virtual Machine Specification
Comments
Runtime Triage: This is not on our current list of priorities. We will consider this feature if we receive additional customer requirements.
10-01-2024

Runtime Triage: This is not on our current list of priorities. We will consider this feature if we receive additional customer requirements.
10-01-2024