Sonntag, 5. August 2018

Hands on Java 11's constantdynamic

With the intention of making the JVM more appealing to dynamic languages, the seventh version of the platform had introduced invokedynamic to its instruction set. Java developers do not normally take note of this feature as it is hidden in Java byte code. In short, by using invokedynamic it has become possible to delay the binding of a method call until its first invocation. This technique is for example used by the Java language to implement lambda expressions which are only manifested on demand upon their first use. Doing so, invokedynamic has evolved into an essential language feature which I have described in detail in a previous blog posting. With constantdynamic a similar mechanism was introduced to Java 11, only that it delays the creation of a constant value. This posting describes the purpose and the inner workings of this feature and shows how to generate code that makes use of this new instruction using the Byte Buddy library.

What are constant values in Java?


Before Java 5, constant values in a Java programs could only be strings or of a primitive type. Those constants were built into the language as literals and are even assumed by the javac compiler to reduce the size of a class file. For example, in the following code snippet the value of the only field is never actually read but instead copied to its use site during compilation:

class ConstantSample {
  final String field = “foo”;
  void hello() {
    System.out.print(field);
  }
}

Instead of reading the field within the hello method, the generated byte code will contain a direct reference to the constant value foo. As a matter of fact, the above class will not ever attempt to read the field’s value what can be validated by altering it using Java reflection after which invoking hello would still print foo.

To represent such constant values, any Java class file contains a constant pool which can be thought of as a table that writes out any constant values that exist within the scope of a class. This implies constants that are used within methods or as field values but also other immutable information that describes a class such as the class’s name or names of invoked methods and their declaring type names. Once a value is recorded in the class’s constant pool, values can be referenced by an offset pointing to a specific entry within the constant pool. Doing so, values that are repeated throughout a class only need to be stored once because an offset can of course be referenced multiple times.

Therefore, when the field is read in the above source code, javac emits a byte code that refers to the offset of the value foo in the constant pool instead of emitting a read instruction for the field. This can be done as the field is declared final where javac ignores the edge-case of a reflective value change. By emitting an instruction to read a constant, javac also saves some bytes compared to an instruction for a field read. This is what makes this optimization lucrative, especially since string and numeric values are fairly commonly in any Java class. Smaller class files help the Java runtime to load classes quicker and an explicit notion of constantness helps the the JVM’s JIT and AOT compilers to apply further optimizations.

The described reuse of offsets for the same constant also implies an identity of reused values. As a consequence of representing an equal string value by a single instance the following statement will assert true in Java:

assert “foo” == “foo”;

Under the hood, both values of foo point to the same constant pool offset in the defining class’s constant pool. Additionally, the JVM even deduplicates constant strings across classes by interning strings that are found in constant pools.

Limitations of constant pool storage


Such tabular representation of values within a class file’s constant pool works well for simple values such as strings and numeric primitives. But at the same time, it can have non-intuitive consequences when javac is not discovering a value as being constant. For example, in the following class the only field’s value is not treated as a constant within the hello method:

class NoConstantSample {
  final String field = “foo”.toString();
  void hello() {
    System.out.print(field);
  }
}

While the toString method is trivial for strings, this circumstance remains unknown to javac which does not evaluate Java methods. Therefore, the compiler can no longer emit a constant pool value as the input to the print statement. Instead, it must emit a field read instruction of the field which requires additional bytes as it was mentioned before. This time, if the field’s value was changed by using reflection, invoking hello would therefore also print the updated value.

Of course, this example is contrived. But it is not difficult to imagine how limiting the classical approach to constants in Java plays out in practice. For example, imagine an integer value that is defined as Math.max(CONST_A, CONST_B). Of course, the maximum of two compile-time constants would itself be constant. Yet, due to javac’s inability of evaluating Java methods, the derived value is not discovered as a constant but only computed at runtime.

Another problem of declaring constant values in a class file’s constant pool, is its limitation to simple values. Strings and numerical values are of course trivial to represent, but more complex Java objects require more flexibility than the classical approach. To support additional constants, the Java class file format already added class literal constants in Java 5 where values such as String.class would no longer be compiled to a call to Class.forName("java.lang.String") but to a constant pool entry containing a class reference. And also the Java 7 release added new constant pool types to the class file specification to allow for a constant representation of MethodType and MethodHandle instances.

In contrast to strings, classes and primitive values, the Java programming language does however not offer a literal for creating those latter constants. Rather, the possibility for such constants was added to better support invokedynamic instructions where javac required an efficient way of representation. In essence, a lambda expression is described by the lambda’s expressions type signature - a MethodType - and a reference to its implementation - a MethodHandle. If both values had to be created as explicit, non-constant arguments for each call to a lambda expression, the performance overhead of using such expressions would surely have outweighed their benefit.

While this solution eased some intermediate pain, it implied a dissatisfying perspective onto Java’s future with regards of adding further constant types. A constant pool entry’s type is encoded by a single byte what severely limits the total number of possible constant types in a class file. As an additional hassle, changes to the class file format require a cascading adjustment of any tool that processes class files what makes a more generic approach for expressing constant values desirable. By introducing constantdynamic, such a mechanism is finally supported by the Java virtual machine with the upcoming release of Java 11.

Introducing dynamic constants


A dynamic constant is not created by processing a literal expression but by invoking a so-called bootstrap method that produces the constant value as its result. This is fairly similar to the invokedynamic instruction that binds method call sites by invoking a bootstrap method during runtime where a pointer to a target implementation for the dynamically bound call site is returned. As key difference, a bootstrapped constant is however immutable whereas dynamically bound method calls can be redirected to another implementation at a later point.

In essence, bootstrap methods are nothing more but Java methods with some requirements to their signature. As a first argument, any bootstrapping method receives a MethodHandles.Lookup instance that is automatically provided by the JVM. Such lookups give access with the privileges of the class that a particular instance of the class represents. For example, when MethodHandles.lookup() is invoked from any class, the caller-sensitive method returns an instance that for example allows for reading private fields of the calling class what would not be possible for a lookup instance that was created from within another class. In the case of a bootstrap method, the lookup represents the class that defines the dynamic constant under creation rather then the class that is declaring the boostrap method. Doing so, the bootstrap methods can access the same information as if the constant was created from within the constant-defining class itself. As a second argument, the bootstrap method receives the constant’s name and as a third argument, it receives the constants expected type. A bootstrap method must be static or a constructor where the constructed value represents the constant.

In many cases, none of these three arguments are required for implementing a bootstrap method but their existence allows for the implementation of more generic bootstrapping mechanisms to facilitate allow for the reuse of bootstrap methods for the creation of multiple constants. If desired, the last two arguments can also be omitted when declaring a bootstrap method. Declaring a MethodHandles.Lookup type as the first parameter is however required. This is done to allow potentially allowing further invocation modes in the future where the first parameter serves as a marker type. This is another difference to invokedynamic which allows allows for the omission of the first parameter.

With this knowledge, we can now express the previous maximum of two constants that was previously mentioned as a derived constant. The value is computed trivially by the following bootstrap method:

public class Bootstrapper {
  public static int bootstrap(MethodHandles.Lookup lookup, String name, Class type) {
    return Math.max(CONST_A, CONST_B);
  }
}

Since the lookup instance that is the first argument comes with the privileges of the class that defines the constant, it would also be possible to acquire the values of CONST_A and CONST_B by using this lookup, even if they were not normally visible to the bootstrap method, for example because they were private. The class’s javadoc explains in detail what API needs to be used for locating a field and to read their values.

In order to create a dynamic constant, a bootstrap method must be referenced within a class’s constant pool as an entry of type dynamic constant. As of today, the Java language has no way of creating such an entry and to my knowledge no other language is currently making use of this mechanism either. For this reason, we will look into creating such classes using the code generation library Byte Buddy later in this article. In Java pseudo code which hints constant pool values in comments, a dynamic constant and its bootstrap method would however be referred to as follows:

class DynamicConstant {
  // constant pool #1 = 10
  // constant pool #2 = 20
  // constant pool #3 = constantdyamic:Bootstrapper.bootstrap/maximum/int.class
  final int CONST_A = [constant #1], CONST_B = [constant #2];
  void hello() {
    System.out.print([constant #3]);
  }
}

Once the hello method is executed for the first time, the JVM would resolve the specified constant by invoking the Bootstrapper.bootstrap method with maximum as the constant name and int.class as the requested type for the created constant. After receiving a result from the bootstrap method, the JVM would then substitute any reference to the constant with this result and never invoke the bootstrap method again. This would also be true if the dynamic constant was referenced at multiple sites.

Avoiding custom bootstrap methods


For most cases, creating a dynamic constant does not require the implementation of an individual bootstrap method. To cover the majority of use cases, the JVM-bundled class java.lang.invoke.ConstantBootstraps already implements several generic bootstrap methods that can be used for the creation of most constants. As center piece, the class’s invoke method allows to define a constant by providing a method reference as a factory for a constant value. To make such a generic approach work, bootstrap methods are capable of receiving any number of additional arguments which must themselves be constant values. Those arguments are then included as references to other constant pool entries while describing the entry of the dynamic constant.

Doing so, the above maximum can rather be computed by providing a handle to the Math.max method and the two constant values of CONST_A and CONST_B as additional arguments. The implementation of the invoke method in ConstantBootstraps will then invoke Math.max using the two values and return the result where the bootstrap method is roughly implemented as follows:

class ConstantBootstraps {
  static Object invoke(MethodHandles.Lookup lookup, String name, Class type,
          MethodHandle handle, Object[] arguments) throws Throwable {
    return handle.invokeWithArguments(arguments);
  }
}

When additional arguments are provided to a bootstrap method, they are assigned in their order to every additional method parameter. To allow for more flexible bootstrap methods such as the invoke method above, the last parameter can also be of an Object array type to receive any excess arguments, in this case the two integer values. If a bootstrap method does not accept a provided argument, the JVM will not invoke the bootstrap method but throw a BootstrapMethodError during the failed constant resolution.

Using this approach, the pseudo code to using ConstantBootstraps.invoke would no longer required an individual bootstrap method and rather look as in the following pseudo code:

class AlternativeDynamicConstant {
  // constant pool #1 = 10
  // constant pool #2 = 20
  // constant pool #3 = MethodHandle:Math.max(int,int)
  // constant pool #4 = constantdyamic:ConstantBootstraps.invoke/maximum/int.class/#3,#1,#2
  final int CONST_A = [constant #1], CONST_B = [constant #2];
  void hello() {
    System.out.print([constant #4]);
  }
}

Nested dynamic constants


As mentioned, the arguments of a bootstrap method are required to be other constant pool entries. With dynamic constants being stored in the constant pool, this allows for nesting dynamic constants what makes this feature even more flexible. This comes with the intuitive limitation that the initialization of dynamic constants must not contain a circles. For example, the following bootstrap methods would be called from top to bottom if the Qux value was resolved:

static Foo boostrapFoo(MethodHandles.Lookup lookup, String name, Class type) {
  return new Foo();
}

static Bar boostrapBar(MethodHandles.Lookup lookup, String name, Class type, Foo foo) {
  return new Bar(foo);
}

static Qux boostrapQux(MethodHandles.Lookup lookup, String name, Class type, Bar bar) {
  return new Qux(bar);
}

When the JVM is required to resolve the dynamic constant for Qux, it would first resolve Bar what would again trigger a previous initialization of Foo as each value depends on the previous.

Nesting dynamic constants can also be required when expressing values that are not supported by static constant pool entry types such as a null reference. Before Java 11, a null value could only be expressed as a byte code instruction but not as a constant pool value where the byte code neither implied a type for null. To overcome this limitation, java.lang.invoke.ConstantBootstraps offers several convenience methods such as nullValue that allows bootstrapping a typed null value as a dynamic constant instead. This null value can then be supplied as an argument to another bootstrap method this method expected null as an argument. Similarly, it is not possible to express a primitive type literal such as int.class in the constant pool which can only represent reference types. Instead, javac translates for example int.class to a read of the static Integer.TYPE field which resolves its value of int.class on startup by a native call into the JVM. Again, ConstantBootstraps offers the primitiveType bootstrap method to represent such values easily as dynamic constants instead.

Why should one care about constant values?


All of the above might sound like a technical finesse that does not add much to the Java platform beyond what static fields already provide. However, the potential of dynamic constants is large but still unexplored. As the most obvious use case, dynamic constants can be used to properly implement lazy values. Lazy values are typically used to represent expensive objects only on-demand when they are used. As of today, lazy values are often implemented by using so-called double checked locking, a pattern that is for example implemented by the scalac compiler for its lazy keyword:

class LazyValue {
  volatile ExpensiveValue value;
  void get() {
    T value = this.value;
    if (value == null) {
      synchronized (this) {
        value = this.value;
          if (value == null) {
            value = new ExpensiveValue();
          }
       }
     }
     return value;
  }
}

The above construct requires a volatile read on every read despite the fact that the value never changes once it is initialized. This implies an unnecessary overhead which can be avoided by expressing the lazy value as a dynamic constant that is only bootstrapped if it is ever used. Especially in the Java core libraries this can be useful for delaying the initialization of many values that are never used, for example in the Locale class which initializes values for any supported language despite the fact that most JVMs only ever use the running machines standard language. By avoiding the initialization of such excess values, the JVM can boot up quicker and avoid using memory for dead values.

Another important use case is the availability of constant expressions to optimizing compilers. It is easy to imagine why compilers prefer processing constant values over mutable values. For example, if a compiler can combine two constants, the result of this combination can permanently replace the previous values. This would of course not be possible if the original values could change over time. And while a just-in-time compiler might still assume that mutable values are factually constant at runtime, an ahead-of-time compiler is dependent on some explicit notion of constantness. By assuring that bootstrap methods are side-effect free, future Java version could for example allow for their compile-time evaluation where constantdynamic could serve as a light-weight macro mechanism to widen the scope of native images written in Java using Graal.

Will I ever work with this feature?


When invokedynamic was introduced in Java 7, this new byte code feature was unused from the perspective of the Java language. However, as of Java 8 invokedynamic instructions can be found in most class files as an implementation of lambda expressions. Similarly, Java 11 does not yet use the constantdynamic feature but one can expect that this will change in the future.

During the latest JVMLS several potential APIs for exposing constantdynamic were already discussed (which would also make invokedynamic accessible via an API). This would be especially useful for library authors to allows them to better resolve critical execution paths but could also unlock some potential to improve javac’s constant detection, for example to widen the scope of non-capturing lambda expressions where field or variable access could be substituted by reading a constant value if a constant value was discovered during compilation. Finally, this new mechanism offers potential for future language enhancements such as a lazy keyword that avoids the overhead of the current equivalents in alternative JVM languages.

The constantdynamic feature can also be useful to Java agents that often need to enhance existing classes with additional information. Java agents cannot normally alter a classes by for example adding static fields as this can both interfere with reflection-based frameworks and since class format changes are forbidden on most JVMs when redefining an already loaded class. Neither restriction does however apply to dynamic constants that are added during runtime where a Java agent can now easily tag classes with additional information.

Creating dynamic constants using Byte Buddy


Despite the lack of language support for constantdynamic, JVMs of version 11 are already fully capable of processing class files that contain dynamic constants. Using the byte code generation library Byte Buddy, we can create such class files and load them into an early access build of the JVM.

In Byte Buddy, dynamic constants are represented by instances of JavaConstant.Dynamic. For convenience, Byte Buddy offers factories for any bootstrap method that is declared by the java.lang.invoke.ConstantBoostraps class such as the invoke method that was discussed previously.

For an easy example, the following code creates a subclass of Callable and defines the return value of the call method as a dynamic constant of the sample class. To bootstrap the constant, we are supplying the constructor of Sample to the mentioned invoke method:

public class Sample {
  public static void main(String[] args) throws Throwable {
    Constructor<? extends Callabable<?>> loaded = new ByteBuddy()
      .subclass(Callable.class)
      .method(ElementMatchers.named("call"))
      .intercept(FixedValue.value(JavaConstant.Dynamic.ofInvocation(Sample.class.getConstructor())))
    .make()
    .load(Sample.class.getClassLoader())
    .getLoaded()
    .getConstructor();

    Callable<?> first = loaded.newInstance(), second = loaded.newInstance();
    System.out.println("Callable instances created");
    System.out.println(first.call() == second.call());
  }
  
  public Sample() { 
    System.out.println("Sample instance created"); 
  }
}

If you run the code, note how only one instance of Sample is created as it was explained in this article. Also note how the instance is only created lazily upon the first invocation of the call method and after the creation of the Callable instances.

To run the above code, you currently have to run Byte Buddy with -Dnet.bytebuddy.experimental=true to unlock support for this feature. This changes once Java 11 is finalized and ready for release where Byte Buddy 1.9.0 will be the first version to support Java 11 out of the box. Also, there are still some rough edges in the latest Byte Buddy release when dealing with dynamic constants. Therefore, it is best to build Byte Buddy from the master branch or to use JitPack. To find more about Byte Buddy, visit bytebuddy.net.

Sonntag, 8. April 2018

JDK 11 and proxies in a world past sun.misc.Unsafe

With JDK 11 the first methods of sun.misc.Unsafe are retired. Among them, the defineClass method was removed. This method has been commonly used by code generation frameworks to define new classes in existing class loaders. While this method was convenient to use, its existence also rendered the JVM inherently unsafe, just as the name of its defining class suggests. By allowing a class to be defined in any class loader and package, it became possible to gain package-scoped access to any package by defining a class within it, thus breaching the boundaries of an otherwise encapsulated package or module.

With the goal of removing sun.misc.Unsafe, the OpenJDK started offering an alternative for defining classes at runtime. Since version 9, the MethodHandles.Lookup class offers a method defineClass similar to the unsafe version. However, the class definition is only permitted for a class that resides in the same package as the lookup's hosting class. As a module can only resolve lookups for packages that are owned by a module or that are opened to it, classes can no longer be injected into packages that did not intend to give such access.

Using method handle lookups, a class foo.Qux can be defined during runtime as follows:
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandles.Lookup privateLookup = MethodHandles.privateLookupIn(foo.Bar.class, lookup);
byte[] fooQuxClassFile = createClassFileForFooQuxClass();
privateLookup.defineClass(fooQuxClassFile);

In order to perform a class definition, an instance of MethodHandles.Lookup is required which can be retrieved by invoking the MethodHandles::lookup method. Invoking the latter method is call site sensitive; the returned instance will therefore represent the privileges of the class and package from within the method is invoked. To define a class in another package then the current one, a class from this package is required to resolve against it using MethodHandles::privateLookupIn. This will only be possible if this target class's package resides in the same module as the original lookup class or if this package is explicitly opened to the lookup class's module. If those requirements are not met, attempting to resolving the private lookup throws an IllegalAccessException, protecting the boundaries that are implied by the JPMS.

Of course, code generation libraries are also constrained by this limitation. Otherwise they could be used to create and inject malicious code. And since the creation of method handles is call-site sensitive, it is not possible to incorporate the new class definition mechanism without requiring users to do some additional work by providing an appropriate lookup instance that represents the privileges of their module.

When using Byte Buddy, the required changes are fortunately minimal. The library defines classes using a ClassDefinitionStrategy which is responsible for the loading a class from its binary format. Prior to Java 11, a class could be defined using reflection or sun.misc.Unsafe using ClassDefinitionStrategy.Default.INJECTION. To support Java 11, this strategy needs to be replaced by ClassDefinitionStrategy.UsingLookup.of(lookup) where the provided lookup must have access to the package in which a classes would reside.

 

Migrating cglib proxies to Byte Buddy


As of today, other code generation libraries do not provide such a mechanism and it is uncertain when and if such capabilities are added. Especially for cglib, API changes have proven problematic in the past due to the libraries old age and widespread use in legacy applications that are no longer updated and would not adopt modifications. For users that want to adopt Byte Buddy as a more modern and actively developed alternative, the following segment will therefore describe a possible migration.

As an example, we generate a proxy for the following sample class with a single method:
public class SampleClass {
  public String test() { 
    return "foo"; 
  }
}

To create a proxy, the proxied class is normally subclassed where all methods are overridden to dispatch the interception logic. Doing so, we append a value bar to the return value of the original implementation as an example.

A cglib proxy is typically defined using the Enhancer class in combination with an MethodInterceptor. A method interceptor supplies the proxied instance, the proxied method and its arguments. Finally, it also provides an instance of MethodProxy which allows to invoke the original code.
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(SampleClass.class);
enhancer.setCallback(new MethodInterceptor() {
  @Override
  public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) {
    return proxy.invokeSuper(obj, method, args) + "bar";
  }
});
SampleClass proxy = (SampleClass) enhancer.create();
assertEquals("foobar", proxy.test());

Note that the above code will cause a problem if any other method such as hashCode, equals or toString was invoked on the proxy instance. The first two methods would also be dispatched by the interceptor and therefore cause a class cast exception when cglib attemted to return the string-typed return value. In contrast, the toString method would work but return an unexpected result as the original implementation was prefixed to bar as a return value.

In Byte Buddy, proxies are not a dedicated concept but can be defined using the library’s generic code generation DSL. For an approach that is the most similar to cglib, using a MethodDelegation offers the easiest migration path. Such a delegation targets a user-defined interceptor class to which method calls are dispatched:
public class SampleClassInterceptor {
  public static String intercept(@SuperCall Callable<String> zuper) throws Exception {
    return zuper.call() + "bar";
  }
}

The above interceptor first invokes the original code via a helper instance that is provided by Byte Buddy on demand. A delegation to this interceptor is implemented using Byte Buddy’s code generation DSL as follows:
SampleClass proxy = new ByteBuddy()
  .subclass(SampleClass.class)
  .method(ElementMatchers.named("test"))
  .intercept(MethodDelegation.to(SampleClassInterceptor.class))
  .make()
  .load(someClassLoader, ClassLoadingStrategy.UsingLookup.of(MethodHandles
      .privateLookupIn(SampleClass.class, MethodHandles.lookup()))
  .getLoaded()
  .getDeclaredConstructor()
  .newInstance();
assertEquals("foobar", proxy.test());

Other than cglib, Byte Buddy requires to specify a method filter using an ElementMatcher. While filtering is perfectly possible in cglib, it is quite cumbersome and not explicitly required and therefore easily forgotten. In Byte Buddy, all methods can still be intercepted using the ElementMatchers.any() matcher but by requiring to specify such a matcher, users are hopefully reminded to make a meaningful choice.

With the above matcher, any time a method named test is invoked, the call will be delegated to the specified interceptor using a method delegation as discussed.

The interceptor that was introduced would however fail to dispatch methods that do not return a string instance. As a matter of fact, the proxy creation would yield an exception issued by Byte Buddy. It is however perfectly possible to define a more generic interceptor that can be applied to any method similar to the one offered by cglib's MethodInterceptor:
public class SampleClassInterceptor {
  @RuntimeType
  public static Object intercept(
      @Origin Method method,
      @This Object self,
      @AllArguments Object[] args,
      @SuperCall Callable<String> zuper
  ) throws Exception {
    return zuper.call() + "bar";
  }
}

Of course, since the additional arguments of the interceptor are not used in this case, they can be omitted what renders the proxy more efficient. Byte Buddy will only provide arguments on demand and if they are actually required.

As the above proxy is stateless, the interception method is defined to be static. Again, this is an easy optimization as Byte Buddy otherwise needs to define a field in the proxy class that holds a reference to the interceptor instance. If an instance is however required, a delegation can be directed to a member method of an instance using MethodDelegation.to(new SampleClassInterceptor()).

 

Caching proxy classes for performance


When using Byte Buddy, proxy classes are not automatically cached. This means that a new class is generated and loaded every time the above code is run. With code generation and class definition being expensive operations, this is of course inefficient and should be avoided if proxy classes can be reused. In cglib, a previously generated class is returned if the input is identical for two enhancements what is typically true when running the same code segment twice. This approach is however rather error prone and often inefficient since a cache key can normally be calculated much easier. With Byte Buddy, a dedicated caching library can be used instead, if such a library already is available. Alternatively, Byte Buddy also offers a TypeCache that implements a simple cache for classes by a user-defined cache key. For example, the above class generation can be cached using the base class as a key using the following code:
TypeCache<Class<?>> typeCache = new TypeCache<>(TypeCache.Sort.SOFT);
Class<?> proxyType = typeCache.findOrInsert(classLoader, SampleClass.class, () -> new ByteBuddy()
  .subclass(SampleClass.class)
  .method(ElementMatchers.named("test"))
  .intercept(MethodDelegation.to(SampleClassInterceptor.class))
  .make()
  .load(someClassLoader, ClassLoadingStrategy.UsingLookup.of(MethodHandles
      .privateLookupIn(SampleClass.class, MethodHandles.lookup()))
  .getLoaded()
});

Unfortunately, caching classes in Java brings some caveats. If a proxy is created, it does of course subclass the class it proxies what makes this base class ineligible for garbage collection. Therefore, if the proxy class was referenced strongly, the key would also be referenced strongly. This would render the cache useless and open for memory leaks. Therefore, the proxy class must be referenced softly or weakly what is specified by the constructor argument. In the future, this problem might be resolved if Java introduced ephemerons as a reference type. At the same time, if garbage collection of proxy classes is not an issue, a ConcurrentMap can be used to compute a value on absence.

 

Broaden the usability of proxy classes


To embrace reuse of proxy classes, it is often meaningful to refactor proxy classes to be stateless and to rather isolate state into an instance field. This field can then be accessed during the interception using the mentioned dependency injection mechanism, for example, to make the suffix value configurable per proxy instance:
public class SampleClassInterceptor {
  public static String intercept(@SuperCall Callable<String> zuper, 
        @FieldValue("qux") String suffix) throws Exception {
    return zuper.call() + suffix;
  }
}

The above interceptor now receives the value of a field qux as a second argument which can be declared using Byte Buddy’s type creation DSL:
TypeCache<Class<?>> typeCache = new TypeCache<>(TypeCache.Sort.SOFT);
Class<?> proxyType = typeCache.findOrInsert(classLoader, SampleClass.class, () -> new ByteBuddy()
    .subclass(SampleClass.class)
    .defineField(“qux”, String.class, Visibility.PUBLIC)
    .method(ElementMatchers.named("test"))
    .intercept(MethodDelegation.to(SampleClassInterceptor.class))
    .make()
    .load(someClassLoader, ClassLoadingStrategy.UsingLookup.of(MethodHandles
        .privateLookupIn(SampleClass.class, MethodHandles.lookup()))
    .getLoaded()
});

The field value can now be set on an every instance after its creation using Java reflection. In order to avoid reflection, the DSL can also be used to implement some interface that declares a setter method for the mentioned field which can be implemented using Byte Buddy's FieldAccessor implementation.

 

Weighting Proxy runtime and creation performance


Finally, when creating proxies using Byte Buddy, some performance considerations need to be made. When generating code, there exists a trade-off between the performance of the code generation itself and the runtime performance of the generated code. Byte Buddy typically aims for creating code that runs as efficiently as possible what might require additional time for the creation of such code compared to cglib or other proxing libraries. This bases on the assumption that most applications run for a long time but only creates proxies a single time what does however not hold for all types of applications.

As an important difference to cglib, Byte Buddy generates a dedicated super call delegate per method that is intercepted rather then a single MethodProxy. These additional classes take more time to create and load but having these classes available results in better runtime performance for each method execution. If a proxied method is invoked in a loop, this difference can quickly be crucial. If runtime performance is however not a primary goal and it is more important that the proxy classes are created in short time, the following approach avoids the creating of additional classes altogether:
public class SampleClassInterceptor {
  public static String intercept(@SuperMethod Method zuper, 
        @This Object target, 
        @AllArguments Object[] arguments) throws Exception {
    return zuper.invoke(target, arguments) + "bar";
  }
}

 

Proxies in a modularized environment


Using the simple form of dependency-injection for interceptors rather then relying on a library-specific type such as cglib's MethodInterceptor, Byte Buddy facilitates another advantage in a modularized environment: since the generated proxy class will reference the interceptor class directly rather then referencing a library specific dispatcher type such as cglib's MethodInterceptor, the proxied class’s module does not need to read Byte Buddy’s module. With cglib, the proxied class module must read cglib's module which defines the MethodInterceptor interface rather then the module that implements such an interface. This will most likely be non-intuitive for users of a library that uses cglib as a transitive dependency, especially if the latter dependency is treated as an implementation detail that should not be exposed.

In some cases, it might not even be possible or desirable that the proxied class's module reads the module of the framework which supplies the interceptor. For this case, Byte Buddy also offers a solution to avoid such a dependency altogether by using its Advice component. This component works on code templates such as that in the following example:
public class SampleClassAdvice {
  @Advice.OnMethodExit
  public static void intercept(@Advice.Returned(readOnly = false) String returned) {
    returned += "bar";
  }
}

The above code might not appear to make much sense as it stands and as a matter of fact, it will never be executed. The class merely serves as a byte code template to Byte Buddy which reads the byte code of the annotated method which is then inlined into the generated proxy class. To do so, every parameter of the above method must be annotated to represent a value of the proxied method. In the above case, the annotation defines the parameter to define the method’s return value to which bar is appended as a suffix given the template. Given this advice class, a proxy class could be defined as follows:
new ByteBuddy()
  .subclass(SampleClass.class)
  .defineField(“qux”, String.class, Visibility.PUBLIC)
  .method(ElementMatchers.named(“test”))
  .intercept(Advice.to(SampleClassAdvice.class).wrap(SuperMethodCall.INSTANCE))
  .make()

By wrapping the advice around a SuperMethodCall, the above advice code will be inlined after the call to the overridden method has been made. To inline code before the original method call, the OnMethodEnter annotation can be used.

Supporting proxies on Java versions prior to 9 and past 10


When developing applications for the JVM, one can normally rely on applications that run on a particular version to also run on later versions. This has been true for a long time, even if internal API has been used. However, as a consequence of removing this internal API, this is no longer true as of Java 11 where code generation libraries that have relied on sun.misc.Unsafe will no longer work. At the same time, class definition via MethodHandles.Lookup is not available to JVMs prior to version 9.

As for Byte Buddy, it is the responsibility of a user to use a class loading strategy that is compatible with the current JVM. To support all JVMs, the following selection needs to be made:
ClassLoadingStrategy<ClassLoader> strategy;
if (ClassInjector.UsingLookup.isAvailable()) {
  Class<?> methodHandles = Class.forName("java.lang.invoke.MethodHandles");
  Object lookup = methodHandles.getMethod("lookup").invoke(null);
  Method privateLookupIn = methodHandles.getMethod("privateLookupIn", 
      Class.class, 
      Class.forName("java.lang.invoke.MethodHandles$Lookup"));
  Object privateLookup = privateLookupIn.invoke(null, targetClass, lookup);
  strategy = ClassLoadingStrategy.UsingLookup.of(privateLookup);
} else if (ClassInjector.UsingReflection.isAvailable()) {
  strategy = ClassLoadingStrateg.Default.INJECTION;
} else {
  throw new IllegalStateException(“No code generation strategy available”);
}

The above code uses reflection to resolve a method handle lookup and to resolve it. Doing so, the code can be compiled and loaded on JDKs prior to Java 9. Unfortunately, Byte Buddy cannot implement this code as a convenience since MethodHandles::lookup is call site sensitive such that the above must be defined in a class that resides in the user’s module and not within Byte Buddy.

Finally, it is worth considering to avoid class injection altogether. A proxy class can also be defined in a class loader of its own using the ClassLoadingStrategy.Default.WRAPPER strategy. This strategy is not using any internal API and will work on any JVM version. However, one must keep in mind the performance costs of creating a dedicated class loader. And finally, even if the package name of the proxy class is equal to the proxied class, by defining the proxy in a different class loaders, their runtime packages will no longer be considered as equal by the JVM thus not allowing to override any package-private methods.

Final thoughts


On a final note I want to express my opinion that retiring sun.misc.Unsafe is an important step towards a safer, modularized JVM despite the costs of this migration. Until this very powerful class is removed, any boundaries set by the JPMS can be circumvented by using the privileged access that sun.misc.Unsafe still offers. Without this removal, the JPMS costs all the inconvenience of additional encapsulation without the benefit of being able to rely on it.

Most developers on the JVM will most likely never experience any problems with these additional restrictions but as described, code generation and proxying libraries need to adapt these changes. For cglib, this does unfortunately mean that the end of the road is reached. Cglib was originally modeled as a more powerful version of Java's built-in proxying API where it requires its own dispatcher API to be referenced by the proxy class similar to how Java's API requires referencing of its types. However, these latter types reside in the java.base module which is always read by any module. For this reason, the Java proxying API still functions whereas the cglib model was broken irreparably. In the past, this has already made cglib a difficult candidate for OSGi enviornments but with the JPMS, cglib as a library does no longer function. A similar problem exists for the corresponding proxying API that is provided by Javassist.

The upside of this change is that the JVM finally offers a stable API for defining classes during an application's runtime, a common operation that has relied on internal API for over twenty years. And with the exception of Javaagents that I think still require a more flexible approach, this means that future Java releases are guaranteed to always work once all users of proxies completed this final migration. And given that the development of cglib has been dormant for years with the library suffering many limitations, an eventual migration by today's users of the library was inevitable in any case. The same might be true for Javassist proxies, as the latter library has not seen commits in almost a half year either.