Search code examples
javaclassloaderbytecode

Why does Class.forName appear in the bytecode for this class?


I've been doing some personal research in Java bytecode and I came across a bit of an oddity. If I decompile this class, I find a reference to Class.forName() hanging out in the constant pool. However, there is no reference in the source code to this method.

I assume that something about this code is causing javac to emit a bit of code that dynamically loads a class, but I'm not sure why this happens. It strikes me as inefficient, but mainly I'm just curious as to why this happens.


Solution

  • After disassembling the code with javap, I noticed that there is a method that doesn't exist in the source code:

    static java.lang.Class class$(java.lang.String);
      Code:
         0: aload_0
         1: invokestatic  #1                  // Method java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
         4: areturn
         5: astore_1
         6: new           #3                  // class java/lang/NoClassDefFoundError
         9: dup
        10: invokespecial #4                  // Method java/lang/NoClassDefFoundError."<init>":()V
        13: aload_1
        14: invokevirtual #5                  // Method java/lang/NoClassDefFoundError.initCause:(Ljava/lang/Throwable;)Ljava/lang/Throwable;
        17: athrow
      Exception table:
         from    to  target type
             0     4     5   Class java/lang/ClassNotFoundException
    

    It looks like this is generated in bytecode compiled for version < JDK1.5 whenever there is a class literal referenced in the code [1]. Basically, this:

    if (getClass() == Level.class) {}
    

    turns into this:

    if (getClass() == class$("org.apache.log4j.Level")) {}
    

    and class$() looks like this:

    static Class class$(java.lang.String className) {
        try {
           return Class.forName(className);
        } catch (ClassNotFoundException e) {
           throw new NoClassDefFoundError();
        }
    }
    

    Apparently in JDK1.5, the ldc_w instruction was given the ability to load class constants and the class$() method was no longer necessary.