user1745356
user1745356

Reputation: 4673

When a class is loaded in JVM

I got two examples:

Example 1:

public class A {

}

public class B {

  public void m(A a) {

  }

}
public class C {

    public static void main(String[] args) {
            B b = new B();
            System.out.println("hello!");
    }

}

Compile all three classes. Remove A.class. Run main. No exception is thrown.

Example 2:

public class D {

}

public class E {

    public void omg(D d) {

    }

    public static void main(String[] args) {
        E e = new E();
    }


}

Compile the classes. Remove D.class. Run main method.

Exception in thread "main" java.lang.NoClassDefFoundError: D
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Unknown Source)
    at java.lang.Class.getMethod0(Unknown Source)
    at java.lang.Class.getMethod(Unknown Source)
    at sun.launcher.LauncherHelper.getMainMethod(Unknown Source)
    at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)
Caused by: java.lang.ClassNotFoundException: D
    at java.net.URLClassLoader$1.run(Unknown Source)

Why? D is never referenced.

Upvotes: 5

Views: 360

Answers (2)

Holger
Holger

Reputation: 298113

Your class has a reference to the class D within the method public void omg(D d).

Normally the JVM from Sun/Oracle uses lazy resolution, so it wouldn’t matter as long as you don’t use that method, however, you can see from the stack trace that the main method is searched via the reflective operation Class.getMethod which makes the difference.

You can verify this with the following code:

public class B {

  public static void main(String[] args) {
    D.main(args);
  }
}
class D {

  public void foo(E e) {}
  public static void main(String[] args) {
    System.out.println("hello world");
  }
}
class E { }

Here, removing E.class after compiling and running java B will not produce an error. Now insert a reflective method lookup into the code:

public class B {

  public static void main(String[] args) {
    try {
      D.class.getMethod("main", String[].class);
    } catch(NoSuchMethodException ex) {
      ex.printStackTrace();
    }
    D.main(args);
  }
}
class D {

  public void foo(E e) {}
  public static void main(String[] args) {
    System.out.println("hello world");
  }
}
class E { }

Now, removing the class E after compiling produces java.lang.NoClassDefFoundError: E when you run with java B. So the manually triggered method lookup reproduces the behavior as in your original code example though class D is not the main class here.


Note that you can fix the problem by removing the public modifier from the method foo. The reason is that Class.getMethod only considers public methods and skips all others.

This also applies to your original code example: removing public from the method omg will make the NoClassDefFoundError disappear.

Upvotes: 2

mindex
mindex

Reputation: 1636

Both are allowed by JavaVM specification. In Chapter 5. Loading, Linking, and Initializing we have:

For example, a Java Virtual Machine implementation may choose to resolve each symbolic reference in a class or interface individually when it is used ("lazy" or "late" resolution), or to resolve them all at once when the class is being verified ("eager" or "static" resolution).

My wild guess is that Sun/Oracle chose to do the "static" resolution for initial("main") class because it's likely that methods in the main class are to be invoked very soon.

Upvotes: 3

Related Questions