< prev index next >
src/java.base/share/classes/java/lang/invoke/MethodHandles.java
Print this page
@@ -25,10 +25,11 @@
package java.lang.invoke;
import jdk.internal.access.JavaLangAccess;
import jdk.internal.access.SharedSecrets;
+import jdk.internal.misc.VM;
import jdk.internal.module.IllegalAccessLogger;
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
@@ -1823,11 +1824,11 @@
* </ul>
*
* <p> If the {@code initialize} parameter is {@code true},
* then {@code C} is initialized by the Java Virtual Machine.
*
- * <p>The newly created class or interface {@code C} is <em>hidden</em>, in the sense that
+ * <p> The newly created class or interface {@code C} is <em>hidden</em>, in the sense that
* no other class or interface can refer to {@code C} via a constant pool entry.
* That is, a hidden class or interface cannot be named as a supertype, a field type,
* a method parameter type, or a method return type by any other class.
* This is because a hidden class or interface does not have a binary name, so
* there is no internal form available to record in any class's constant pool.
@@ -1838,27 +1839,64 @@
* {@link ClassLoader#loadClass(String, boolean)}, or {@link #findClass(String)}, and
* is not {@linkplain java.lang.instrument.Instrumentation#isModifiableClass(Class)
* modifiable} by Java agents or tool agents using the <a href="{@docRoot}/../specs/jvmti.html">
* JVM Tool Interface</a>.
*
- * <p> If {@code options} has the {@link ClassOption#NESTMATE NESTMATE} option, then
- * the newly created class or interface {@code C} is a member of a nest. The nest
- * to which {@code C} belongs is not based on any {@code NestHost} attribute in
- * the {@code ClassFile} structure from which {@code C} was derived.
+ * <p> A class or interface created by
+ * {@linkplain ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)
+ * a class loader} has a strong relationship with that class loader.
+ * That is, every {@code Class} object contains a reference to the {@code ClassLoader}
+ * that {@linkplain Class#getClassLoader() defined it}.
+ * This means that a class created by a class loader may be unloaded if and
+ * only if its defining loader is not reachable and thus may be reclaimed
+ * by a garbage collector (JLS 12.7).
+ *
+ * By default, however, a hidden class or interface may be unloaded even if
+ * the class loader that is marked as its defining loader is
+ * <a href="../ref/package.html#reachability">reachable</a>.
+ * This behavior is useful when a hidden class or interface serves multiple
+ * classes defined by arbitrary class loaders. In other cases, a hidden
+ * class or interface may be linked to a single class (or a small number of classes)
+ * with the same defining loader as the hidden class or interface.
+ * In such cases, where the hidden class or interface must be coterminous
+ * with a normal class or interface, the {@link ClassOption#STRONG STRONG}
+ * option may be passed in {@code options}.
+ * This arranges for a hidden class to have the same strong relationship
+ * with the class loader marked as its defining loader,
+ * as a normal class or interface has with its own defining loader.
+ *
+ * If {@code STRONG} is not used, then the invoker of {@code defineHiddenClass}
+ * may still prevent a hidden class or interface from being
+ * unloaded by ensuring that the {@code Class} object is reachable.
+ *
+ * <p> The unloading characteristics are set for each hidden class when it is
+ * defined, and cannot be changed later. An advantage of allowing hidden classes
+ * to be unloaded independently of the loader deemed as their defining loader
+ * is that a very large number of hidden classes may be created by an application.
+ * In contrast, if {@code STRONG} is used, then the JVM may run out of memory,
+ * just as if normal classes were created by class loaders.
+ *
+ * <p> Classes and interfaces in a nest are allowed to have mutual access to
+ * their private members. The nest relationship is determined by
+ * the {@code NestHost} attribute (JVMS 4.7.28) and
+ * the {@code NestMembers} attribute (JVMS 4.7.29) in a {@code class} file.
+ * By default, a hidden class belongs to a nest consisting only of itself
+ * because a hidden class has no binary name.
+ * The {@link ClassOption#NESTMATE NESTMATE} option can be passed in {@code options}
+ * to create a hidden class or interface {@code C} as a member of a nest.
+ * The nest to which {@code C} belongs is not based on any {@code NestHost} attribute
+ * in the {@code ClassFile} structure from which {@code C} was derived.
* Instead, the following rules determine the nest host of {@code C}:
* <ul>
* <li>If the nest host of the lookup class of this {@code Lookup} has previously
- * been determined, then {@code H} be the nest host of the lookup class.</li>
- * <li>Otherwise, it is determined using the algorithm in JVMS 5.4.4, yielding {@code H}.</li>
- * <li>The nest host of {@code C} is determined to be {@code H}, the nest host of the lookup class.</li>
+ * been determined, then let {@code H} be the nest host of the lookup class.
+ * Otherwise, the nest host of the lookup class is determined using the
+ * algorithm in JVMS 5.4.4, yielding {@code H}.</li>
+ * <li>The nest host of {@code C} is determined to be {@code H},
+ * the nest host of the lookup class.</li>
* </ul>
*
- * <p> If {@code options} has {@link ClassOption#WEAK WEAK} option, then
- * the newly created class or interface is <em>not strongly referenced</em> from
- * its defining class loader. Therefore, it may be unloaded while
- * its defining class loader is strongly reachable.
- *
* <p> A hidden class or interface may be serializable, but this requires a custom
* serialization mechanism in order to ensure that instances are properly serialized
* and deserialized. The default serialization mechanism supports only classes and
* interfaces that are discoverable by their class name.
*
@@ -1888,15 +1926,18 @@
*
* @since 15
* @see Class#isHiddenClass()
* @jvms 4.2.1 Binary Class and Interface Names
* @jvms 4.2.2 Unqualified Names
+ * @jvms 4.7.28 The {@code NestHost} Attribute
+ * @jvms 4.7.29 The {@code NestMembers} Attribute
* @jvms 5.4.3.1 Class and Interface Resolution
* @jvms 5.4.4 Access Control
* @jvms 5.3.5 Deriving a {@code Class} from a {@code class} File Representation
* @jvms 5.4 Linking
* @jvms 5.5 Initialization
+ * @jls 12.7 Unloading of Classes and Interfaces
*/
public Lookup defineHiddenClass(byte[] bytes, boolean initialize, ClassOption... options)
throws IllegalAccessException
{
Objects.requireNonNull(bytes);
@@ -1906,11 +1947,11 @@
if (!hasFullPrivilegeAccess()) {
throw new IllegalAccessException(this + " does not have full privilege access");
}
Set<ClassOption> opts = options.length > 0 ? Set.of(options) : Set.of();
- return makeHiddenClassDefiner(bytes.clone(), opts).defineClassAsLookup(initialize);
+ return makeHiddenClassDefiner(bytes.clone(), opts, false).defineClassAsLookup(initialize);
}
/**
* Creates a <em>hidden</em> class or interface from {@code bytes} with {@code classData},
* returning a {@code Lookup} on the newly created class or interface.
@@ -1959,52 +2000,101 @@
if (!hasFullPrivilegeAccess()) {
throw new IllegalAccessException(this + " does not have full privilege access");
}
Set<ClassOption> opts = options.length > 0 ? Set.of(options) : Set.of();
- return makeHiddenClassDefiner(bytes.clone(), opts).defineClassAsLookup(initialize, classData);
+ return makeHiddenClassDefiner(bytes.clone(), opts, false)
+ .defineClassAsLookup(initialize, classData);
}
private ClassDefiner makeClassDefiner(byte[] bytes) {
- return new ClassDefiner(this, bytes, Set.of(), 0);
+ return new ClassDefiner(this, bytes, STRONG_LOADER_LINK);
}
/**
* Returns a ClassDefiner that creates a {@code Class} object of a hidden class
- * from the given bytes and options.
+ * from the given bytes.
+ *
+ * @param bytes class bytes
+ * @return ClassDefiner that defines a hidden class of the given bytes.
+ */
+ ClassDefiner makeHiddenClassDefiner(byte[] bytes) {
+ return makeHiddenClassDefiner(bytes, Set.of(), false);
+ }
+
+ /**
+ * Returns a ClassDefiner that creates a {@code Class} object of a hidden class
+ * from the given bytes.
+ *
+ * @param name fully-qualified name that specifies the prefix of the hidden class
+ * @param bytes class bytes
+ * @return ClassDefiner that defines a hidden class of the given bytes.
+ */
+ ClassDefiner makeHiddenClassDefiner(String name, byte[] bytes) {
+ return makeHiddenClassDefiner(name, bytes, Set.of(), false);
+ }
+
+ /**
+ * Returns a ClassDefiner that creates a {@code Class} object of a hidden class
+ * from the given bytes and options. This method will read the class file
+ * and obtain the class name.
*
* @param bytes class bytes
* @param options class options
+ * @param accessVmAnnotations true to give the hidden class access to VM annotations
* @return ClassDefiner that defines a hidden class of the given bytes and options
*/
- ClassDefiner makeHiddenClassDefiner(byte[] bytes, Set<ClassOption> options) {
- return new ClassDefiner(this, bytes, options, HIDDEN_CLASS);
+ ClassDefiner makeHiddenClassDefiner(byte[] bytes,
+ Set<ClassOption> options,
+ boolean accessVmAnnotations) {
+ int flags = HIDDEN_CLASS | ClassOption.optionsToFlag(options);
+ if (accessVmAnnotations | VM.isSystemDomainLoader(lookupClass.getClassLoader())) {
+ // jdk.internal.vm.annotations are permitted for classes
+ // defined to boot loader and platform loader
+ flags |= ACCESS_VM_ANNOTATIONS;
+ }
+
+ return new ClassDefiner(this, bytes, flags);
}
/**
- * This method is only called by MethodHandleImpl.BindCaller.makeInjectedInvoker.
+ * Returns a ClassDefiner that creates a {@code Class} object of a hidden class
+ * from the given bytes and options.
*
* @param name the name of the class and the name in the class bytes is ignored.
* @param bytes class bytes
- * @return ClassDefiner that defines a hidden class of the given bytes
+ * @param options class options
+ * @param accessVmAnnotations true to give the hidden class access to VM annotations
*/
- ClassDefiner makeHiddenClassDefiner(String name, byte[] bytes, int flags) {
- return new ClassDefiner(this, name, bytes, HIDDEN_CLASS|flags);
+ ClassDefiner makeHiddenClassDefiner(String name,
+ byte[] bytes,
+ Set<ClassOption> options,
+ boolean accessVmAnnotations) {
+ int flags = HIDDEN_CLASS | ClassOption.optionsToFlag(options);
+ if (accessVmAnnotations | VM.isSystemDomainLoader(lookupClass.getClassLoader())) {
+ // jdk.internal.vm.annotations are permitted for classes
+ // defined to boot loader and platform loader
+ flags |= ACCESS_VM_ANNOTATIONS;
+ }
+
+ return new ClassDefiner(this, name, bytes, flags);
}
static class ClassDefiner {
private final Lookup lookup;
private final String name;
private final byte[] bytes;
private final int classFlags;
// caller should make a defensive copy of the arguments if needed
// before calling this constructor
- private ClassDefiner(Lookup lookup, byte[] bytes, Set<ClassOption> options, int flags) {
+ private ClassDefiner(Lookup lookup, byte[] bytes, int flags) {
+ // defining an ordinary class which must be a strongly referenced by its defining loader
+ assert ((flags & HIDDEN_CLASS) != 0 || (flags & STRONG_LOADER_LINK) == STRONG_LOADER_LINK);
this.lookup = lookup;
this.bytes = bytes;
- this.classFlags = flags | ClassOption.optionsToFlag(options);
+ this.classFlags = flags;
this.name = className(bytes);
int index = name.lastIndexOf('.');
String pn = (index == -1) ? "" : name.substring(0, index);
if (!pn.equals(lookup.lookupClass().getPackageName())) {
@@ -2013,10 +2103,11 @@
}
}
// skip package name check
private ClassDefiner(Lookup lookup, String name, byte[] bytes, int flags) {
+ assert ((flags & HIDDEN_CLASS) != 0 || (flags & STRONG_LOADER_LINK) == STRONG_LOADER_LINK);
this.lookup = lookup;
this.bytes = bytes;
this.classFlags = flags;
this.name = name;
}
@@ -3739,14 +3830,13 @@
static ConcurrentHashMap<MemberName, DirectMethodHandle> LOOKASIDE_TABLE = new ConcurrentHashMap<>();
/**
* The set of class options that specify whether a hidden class created by
* {@link Lookup#defineHiddenClass(byte[], boolean, ClassOption...)
- * Lookup::defineHiddenMethod} method is dynamically added as
- * a new member to the nest of a lookup class and whether a hidden class
- * is weak such that it may be unloaded while its defining class loader
- * is <a href="../ref/package.html#reachability">strongly reachable</a>.
+ * Lookup::defineHiddenClass} method is dynamically added as a new member
+ * to the nest of a lookup class and/or whether a hidden class has
+ * a strong relationship with the class loader marked as its defining loader.
*
* @since 15
*/
public enum ClassOption {
/**
@@ -3760,18 +3850,26 @@
* @see Class#getNestHost()
*/
NESTMATE(NESTMATE_CLASS),
/**
- * This class option specifies the hidden class be weak so that
- * the class is not strongly referenced by its defining class loader.
- * A weak class may be unloaded while its defining class loader is
- * <a href="../ref/package.html#reachability">strongly reachable</a>.
+ *
+ * This class option specifies the hidden class to have a <em>strong</em>
+ * relationship with the class loader marked as its defining loader,
+ * as a normal class or interface has with its own defining loader.
+ * This means that the hidden class may be unloaded if and only if
+ * its defining loader is not reachable and thus may be reclaimed
+ * by a garbage collector (JLS 12.7).
+ *
+ * <p> By default, a hidden class or interface may be unloaded
+ * even if the class loader that is marked as its defining loader is
+ * <a href="../ref/package.html#reachability">reachable</a>.
+
*
* @jls 12.7 Unloading of Classes and Interfaces
*/
- WEAK(WEAK_CLASS);
+ STRONG(STRONG_LOADER_LINK);
/* the flag value is used by VM at define class time */
private final int flag;
ClassOption(int flag) {
this.flag = flag;
< prev index next >