JDK-8334888 : 5.4: Clarify whether/when "spontaneous" class loading is permitted
  • Type: Bug
  • Component: specification
  • Sub-Component: vm
  • Affected Version: 23
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2024-06-24
  • Updated: 2024-06-25
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
Relates :  
Relates :  
Description
During linking, various "resolution activities" require the loading of classes. Primarily, this is triggered by constant resolution, which specifically calls out the loading of certain named classes. (And, aside from CONSTANT_Dynamic and CONSTANT_InvokeDynamic, the timing of resolution is extremely flexible.) Most of the 5.4 discussion about the timing of linking and, recursively, class loading, seems focused on resolution.

But there are other circumstances in which a class is loaded without a corresponding constant resolution. These other circumstances are sometimes poorly-specified or unspecified. This is of some concern, because class loading attempts are observable to user-defined class loaders, and because class loading failures may (or may not) manifest as user-observable exceptions.

Cases of class loading without resolution that we've identified include:

- The recursive loading of superclasses and superinterfaces during class loading (5.3.5). Unlike most other cases, this is mandated to occur at a specific time, and error case behavior is well-specified.

- Subtype checks during verification. This is somewhat specified with the 'loadedClass' verifier rule, but that's buried in the guts of various verification rules, can sometimes be implementation-dependent (depending on which predicate the implementation chooses to test first), and doesn't address verification by type inference (4.10.2). There's an imprecise mention in 5.4.1 ("Verification may cause additional class and interfaces to be loaded"). One reading of this rule is that verification can load any class it wishes; another reading is that verification can load some classes, but these will be justified by the specification.

- JIT-specific loading of classes in a descriptor (JDK-8334324). There's no specific action in JVMS that this corresponds to, it's just an implementation looking for more information about the parameters to a method. This action may not even correspond to "linking", depending on how we define the term.

- Checking whether an exception should be handled. Per 2.10, implementations must check whether "the exception type is the same class as or a subclass of the class of exception that the exception handler handles". An exception handler "handles" a class by naming it with a CONSTANT_Class. It is not stated that this constant must be *resolved* in order to perform this check, or simply *loaded*. HotSpot performs resolution and throws any resolution error (including, potentially, an IllegalAccessError). Another implementation, J9, simply loads the class without resolution.

- Reflection of attributes. Metadata stored in most attributes, like annotations, is never directly interpreted by the abstract JVM, but user-driven reflection activities may force such attributes to be interpreted and corresponding classes to be loaded. Might we allow an implementation to eagerly load classes mentioned by RuntimeVisibleAnnotations? I'm not sure this has come up in practice, but I'm also not sure the spec provides a definitive answer if it did.

- Various classes are loaded before we get to the "initial class" specified in 5.2, including some core JDK classes and, potentially, a user-defined class loader. 5.2 doesn't have anything to say directly about which classes are loaded before the initial class.

- In the future with value classes (JDK-8317278), the 'LoadableDescriptors' attribute lists field descriptors that the JVM implementation can optionally load, without any corresponding resolution action. The draft JVMS explicitly allows for loading in this case, and specifies that any errors must be ignored. The timing of this loading is specified to occur "during any phase of linking", although that's a bit vague. (Does it allow for loading at JIT compilation time?)

- In the future under Leyden, the set of classes loaded (and even initialized) before 'main' may be user-configurable.

There are probably other cases. HotSpot seems to somewhat freely load classes when it finds doing so to be useful.

We need a consensus on the precise circumstances that justify loading additional classes. This could be very narrow—you can only load classes when the spec permits it—or very permissive—implementations can load any classes, whenever they want. We also need to clarify any appropriate error handling behavior when such loading occurs. Then we need spec language (probably in 5.4?) that captures these rules.
Comments
Thanks for the write-up [~dlsmith] ! I think JIT -Xcomp mode is another example of spontaneous, or at least out-of-order, loading because it is driven by compilation of the class as a whole not on the execution sequence of methods. FWIW my 2c on this is that eager loading is a good/useful thing in general. The error handling is a little awkward in that a user-defined classloader can notice the failed load attempt, but in general (as already specified in places) errors during eager loading should be ignored (obviously the eager activity will be curtailed but the error will not become directly apparent to the user) and only if the class _must_ be loaded later will the error (if it persists) become apparent (and persistent).
25-06-2024

This sentence in 5.4 is a significant source of confusion: > This specification allows an implementation flexibility as to when linking > activities (and, because of recursion, loading) take place One reading is that class loading has the same sort of flexibility that linking activities like resolution enjoy. Another is that the "recursion" mentioned here only applies to specific loading actions mentioned by the spec as part of verification, preparation, or resolution. I considered this change in order to put some distance between the discussion about flexibility and the observation that linking can lead to more loading: > 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. > Linking also involves resolution of symbolic references in the class or > interface, though not necessarily at the same time as the class or interface is > verified and prepared. > **Linking activities may sometimes require the loading of additional classes.** > 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: But I think there's more that needs to be said, and the details will depend on what we think is the right behavior to specify.
24-06-2024