Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8247797

Make annotation parsing in the JDK lazy

    XMLWordPrintable

Details

    • Enhancement
    • Resolution: Unresolved
    • P4
    • None
    • None
    • core-libs
    • None
    • generic
    • generic

    Description

      Currently annotation parsing in the JDK is eager: Calling getAnnotation() on a class, method, field, ... instantiates all annotations of that element with all dependencies (for example enum values that are referenced by the annotation).

      Here is an extreme example:

      import java.lang.annotation.*;

      @MyAnnotation
      public class HelloAnnotation extends SuperClass {
        public static void main(String[] args) {
          HelloAnnotation.class.getAnnotation(MyAnnotation.class);
        }
      }

      @OtherAnnotation
      class SuperClass {
      }

      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.TYPE)
      @interface MyAnnotation {
      }

      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.TYPE)
      @interface OtherAnnotation {
        EnumWithInterface enumWithInterface() default EnumWithInterface.VALUE;
      }

      enum EnumWithInterface implements InterfaceWithDefaultMethod {
        VALUE;
      }

      interface InterfaceWithDefaultMethod {

        SideEffect SIDE_EFFECT = new SideEffect();
        
        default void defaultMethod() {
        }
      }

      class SideEffect {
        static {
          System.out.println("Hello, world!");
        }
      }

      Even though only the particular annotation MyAnnotation is requested, all annotations with all side effects for the whole super-class hierarchy are loaded and instantiated, leading to a long chain of class loading and class initialization.

      In this particular case, the annotation OtherAnnotation is present on the super class, and super class annotations are loaded because of possible @Inherited annotations. OtherAnnotation references an enum, the enum implements an interface with a default method (the default method is important because of class initialization rules for interfaces), and the interface has a static field that is initialized to an instance of a class that has a class initializer.

      So executing the code prints "Hello, world!" (tested on JDK 8, 11, and 15).

      This example is of course a rare corner case. But in practice, problems occur when classes are missing on the class path for annotations or their dependencies. If for example the class file for OtherAnnotation or EnumWithInterface, or anything else further down the dependency chain is not on the class path, then the annotation lookup fails with a NoClassDefFoundError.

      The proper solution is to make all annotation parsing, instantiation, property lookup, ... lazy. This means
      * allowing to access MyAnnotation without loading OtherAnnotation,
      * allowing to access OtherAnnotation without loading EnumWithInterface, so that
      * only invoking OtherAnnotation.enumWithInterface() triggers the side effect of initializing classes.

      Attachments

        Activity

          People

            Unassigned Unassigned
            cwimmer Christian Wimmer
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

              Created:
              Updated: