Java 15 Hidden Classes Explained with Examples
Introduction
Many frameworks generate classes while an application is running: lambda expressions are turned into classes at runtime, dynamic proxies implement interfaces on the fly, and languages such as Groovy, JRuby and Kotlin scripts compile code into classes as they run. These generated classes are implementation details - no other code should find them by name - and they are often needed only briefly. Before Java 15, the JDK had no standard way to define such classes, so frameworks used the internal, unsupported
Defining a hidden class
A hidden class is defined with a
OUTPUT of the above code
Explanation of the code
Who uses hidden classes
Application developers rarely define hidden classes themselves; they benefit from them indirectly -
sun.misc.Unsafe.defineAnonymousClass(). Java 15Click to read tutorial: Overview of New Features in Java 15 (JEP 371) added hidden classes as a supported replacement. This tutorial explains hidden classes: what makes them hidden, why frameworks need them, and how to define one with MethodHandles.Lookup.defineHiddenClass(), with a runnable example.
What makes a class hidden
A hidden class is created from an array of class-file bytes, like any dynamically generated class, but it has three special properties -
- Not discoverable: it is not registered with any class loader under its name, so
Class.forName()cannot find it, and no other class can refer to it in its bytecode - not even by a static type in a method signature. It can only be used through theClassobject returned when it is defined, typically via reflection, method handles or an interface it implements. - Unloadable on its own: a hidden class can be unloaded as soon as it is no longer reachable, even if the class loader which defined it is still in use. Normal classes are only unloaded together with their whole class loader. This avoids memory leaks in frameworks which generate many short-lived classes.
- Access as a nestmate: it can optionally be defined as a nestmateClick to read: nestmate of the class that created it, giving it access to that class's private members.
getName(), has a slash and a unique suffix, such as Greeter/0x0000000801001000 - not a valid binary class name, which guarantees it cannot clash with or be looked up as a normal class.
MethodHandles.Lookup object, whose class becomes the lookup class - the hidden class is created in the same package and with the same access rights. In a real framework, the class bytes would be generated with a bytecode library or the Class-File APIClick to read: Class-File API. To keep the example short, it reads the bytes of an ordinary compiled class, HelloGreeter, and defines a hidden class from them.
An interface the hidden class implements
//Greeter.java
public interface Greeter {
String greet(String name);
}
The class whose bytes are used
//HelloGreeter.java
public class HelloGreeter implements Greeter {
public String greet(String name) {
return "Hello, " + name + " from " + getClass().getName();
}
}
Defining and using a hidden class
//HiddenClassDemo.java
import java.io.InputStream;
import java.lang.invoke.MethodHandles;
public class HiddenClassDemo {
public static void main(String[] args) throws Exception {
byte[] bytes;
try (InputStream in = HiddenClassDemo.class.getResourceAsStream("HelloGreeter.class")) {
bytes = in.readAllBytes(); // stands in for bytes generated at runtime
}
MethodHandles.Lookup lookup = MethodHandles.lookup();
Class<?> hidden = lookup.defineHiddenClass(bytes, true).lookupClass();
System.out.println("isHidden(): " + hidden.isHidden());
System.out.println("Name starts with: " + hidden.getName().substring(0, hidden.getName().indexOf('/') + 1) + "0x...");
Greeter greeter = (Greeter) hidden.getDeclaredConstructor().newInstance();
System.out.println(greeter.greet("Tom").replaceAll("/0x[0-9a-f]+", "/0x..."));
try {
Class.forName(hidden.getName());
} catch (ClassNotFoundException e) {
System.out.println("Class.forName() cannot find it: ClassNotFoundException");
}
System.out.println("The normal HelloGreeter is a different class: " + (hidden != HelloGreeter.class));
}
}
isHidden(): true Name starts with: HelloGreeter/0x... Hello, Tom from HelloGreeter/0x... Class.forName() cannot find it: ClassNotFoundException The normal HelloGreeter is a different class: true
lookup.defineHiddenClass(bytes, true)defines the hidden class and returns a newLookupwhose lookup class is the hidden class. Thetrueargument initializes the class immediately. OptionalClassOptionarguments, such asNESTMATEandSTRONG, control nestmate access and whether the class can be unloaded independently of its class loader.isHidden(), a new method onClass, returnstrue, and the name has a slash and a unique suffix (replaced by "0x..." in the output, since it changes on every run).- The hidden class is used through the
Greeterinterface it implements - the usual way frameworks expose generated code - and itsgetClass().getName()shows the hidden name. Class.forName()cannot find it, even with its exact name, and it is a different class from the normalHelloGreeter, although it was defined from the same bytes.
- Lambdas: since Java 15, the JDK's own
LambdaMetafactorydefines the classes behind lambda expressions as hidden classes. Printing a lambda's class name shows names likeMain$$Lambda/0x.... - Frameworks: bytecode-generating libraries and frameworks, such as those creating proxies or serializers, can use hidden classes instead of internal APIs.
- Language runtimes: JVM languages which compile scripts at runtime can generate many small classes and have them unloaded when no longer needed.
sun.misc.Unsafe.defineAnonymousClass() was deprecated for removal; it was removed in Java 17. Moving frameworks off this internal API was also part of the strong encapsulation of JDK internalsClick to read tutorial: Strong Encapsulation of JDK Internals in Java 16 and 17.
Summary
In this tutorial we saw that hidden classes, added in Java 15, are classes which frameworks generate at runtime and which cannot be discovered by name, can be unloaded independently, and can optionally access their creator's private members. We defined a hidden class from class-file bytes with Lookup.defineHiddenClass(), used it through an interface, and saw that Class.forName() cannot find it. Lastly, we saw that lambdas and frameworks are the main users of hidden classes, replacing the internal Unsafe.defineAnonymousClass(). Next, read the overview of new features in Java 16Click to read tutorial: Overview of New Features in Java 16.Continue Reading Java 12 to 17 Tutorials
Overview of New Features in Java 15Click to read tutorial: Overview of New Features in Java 15 Java Text Blocks: Multi-line Strings with ExamplesClick to read tutorial: Java Text Blocks: Multi-line Strings with Examples Java 15 String Methods: formatted, stripIndent and MoreClick to read tutorial: Java 15 String Methods: formatted, stripIndent and More ZGC and Shenandoah: Low-Pause GCs in Java 15Click to read tutorial: ZGC and Shenandoah: Low-Pause GCs in Java 15 Overview of New Features in Java 12Click to read tutorial: Overview of New Features in Java 12 Overview of New Features in Java 13Click to read tutorial: Overview of New Features in Java 13 Overview of New Features in Java 14Click to read tutorial: Overview of New Features in Java 14 Overview of New Features in Java 16Click to read tutorial: Overview of New Features in Java 16 Overview of New Features in Java 17Click to read tutorial: Overview of New Features in Java 17
Overview of New Features in Java 15Click to read tutorial: Overview of New Features in Java 15 Java Text Blocks: Multi-line Strings with ExamplesClick to read tutorial: Java Text Blocks: Multi-line Strings with Examples Java 15 String Methods: formatted, stripIndent and MoreClick to read tutorial: Java 15 String Methods: formatted, stripIndent and More ZGC and Shenandoah: Low-Pause GCs in Java 15Click to read tutorial: ZGC and Shenandoah: Low-Pause GCs in Java 15 Overview of New Features in Java 12Click to read tutorial: Overview of New Features in Java 12 Overview of New Features in Java 13Click to read tutorial: Overview of New Features in Java 13 Overview of New Features in Java 14Click to read tutorial: Overview of New Features in Java 14 Overview of New Features in Java 16Click to read tutorial: Overview of New Features in Java 16 Overview of New Features in Java 17Click to read tutorial: Overview of New Features in Java 17