Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When does the Java VM try to load Class dependencies?

I'm running into a class loader issue that I don't understand. I've seen the same behavior on OSX with Java 1.6.0 and on Windows XP.

When I run the following code with MyListener and MyObject not in the class path I get a NoClassDefFoundError. However, if I remove the MyObject.add(my) line or replace it with MyObject.add(null) then the code runs fine.

Notice the method with the unresolvable dependencies is never actually used.

I don't understand why MyObject.add(my) causes the VM to try to load MyListener but MyListener my = new MyListener(){}; does not.

public class Main {

    public  void neverCalled(){
        MyListener my = new MyListener(){};
        MyObject.add(my);
    }

    public static void sayHi(){
        System.out.println("Hello");
    }

    public static void main(String[] args) {
        System.out.println("Starting...");
        sayHi();
    }
}

There is nothing interesting about MyObject and MyListener:

public class MyObject {
    public static void add(MyListener in){}
}

public interface MyListener {}

I did some additional research based on the information provided by biaobiaoqi below. Obviously for some unknown reason having a method call with a parameter seems to cause the parameters class to be loaded whereas merely declaring a variable does not.

Section 2.17.1 of the Java VM Spec, 2nd edition says:

The only requirement regarding when resolution is performed is that any errors detected during resolution must be thrown at a point in the program where some action is taken by the program that might, directly or indirectly, require linkage to the class or interface involved in the error

Section 2.17.3 of the Java VM Spec, 2nd edition says:

The Java programming language allows an implementation flexibility as to when linking activities (and, because of recursion, loading) take place, provided that the semantics of the language are respected, that a class or interface is completely verified and prepared before it is initialized, and that errors detected during linkage are thrown at a point in the program where some action is taken by the program that might require linkage to the class or interface involved in the error.

and finally Chapter 8 of Inside The Java Virtual Machine says:

As described in Chapter 7, "The Lifetime of a Class," different implementations of the Java virtual machine are permitted to perform resolution at different times during the execution of a program. An implementation may choose to link everything up front by following all symbolic references from the initial class, then all symbolic references from subsequent classes, until every symbolic reference has been resolved. In this case, the application would be completely linked before its main() method was ever invoked. This approach is called early resolution. Alternatively, an implementation may choose to wait until the very last minute to resolve each symbolic reference. In this case, the Java virtual machine would resolve a symbolic reference only when it is first used by the running program. This approach is called late resolution. Implementations may also use a resolution strategy in-between these two extremes.

Although a Java virtual machine implementation has some freedom in choosing when to resolve symbolic references, every Java virtual machine must give the outward impression that it uses late resolution. No matter when a particular Java virtual machine performs its resolution, it will always throw any error that results from attempting to resolve a symbolic reference at the point in the execution of the program where the symbolic reference was actually used for the first time. In this way, it will always appear to the user as if the resolution were late. If a Java virtual machine does early resolution, and during early resolution discovers that a class file is missing, it won't report the class file missing by throwing the appropriate error until later in the program when something in that class file is actually used. If the class is never used by the program, the error will never be thrown.

On the surface, the behavior I'm seeing seems to violate the JVM specification.

like image 603
Eric Rosenberg Avatar asked Nov 17 '11 22:11

Eric Rosenberg


People also ask

How does class loading happens in Java?

Java ClassLoader MethodsIf the Class is not already loaded, then it will delegate the request to parent ClassLoader to load the class. If the parent ClassLoader doesn't find the class then it will invoke findClass() method to look for the classes in the file system.

Which classes are loaded when JVM starts?

Initially when a JVM starts up, nothing is loaded into it. The class file of the program being executed is loaded first and then other classes and interfaces are loaded as they get referenced in the bytecode being executed.

How are classes loaded by JVM?

In order to actually load a class, the JVM uses Classloader objects. Every already loaded class contains a reference to its class loader, and that class loader is used to load all the classes referenced from that class.

Which component of JVM is responsible for loading a class?

Class loaders are responsible for loading Java classes dynamically to the JVM (Java Virtual Machine) during runtime. They're also part of the JRE (Java Runtime Environment). Therefore, the JVM doesn't need to know about the underlying files or file systems in order to run Java programs thanks to class loaders.


1 Answers

I've test it. When it's MyObject.add(my); , only MyListener is needed , instead of MyObject. And what amazing is :when I replace MyObject.add(my); with System.out.println(my);, nothing error came out. the only different is that the argument type of static method println() is Object , instead of MyListener.

I have searched a lot and find some useful-like information. Let's see the following words,it's from Inside the Java2 Virtual Machine

Class loaders (bootstrap or user-defined) need not wait until a type's first active use before they load the type. Class loaders are allowed to cache binary representations of types, load types early in anticipation of eventual use, or load types together in related groups. If a class loader encounters a problem during early loading, however, it must report that problem (by throwing a subclass of LinkageError) only upon the type's first active use. In other words, if a class loader encounters a missing or malformed class file during early loading, it must wait to report that error until the class's first active use by the program. If the class is never actively used by the program, the class loader will never report the error.

The first half part can answer why there is NoClassDefFoundError. As the JVM can decide by itself when it should load a class, maybe

    MyListener my = new MyListener(){};
    MyObject.add(my);

such a style just makes it load the MyListener interface .

But the second half seems to conflict with that . The neverCalled method actually never be called, there is no active use . I think the only reason can be that is a java1.2 specification .

like image 72
biaobiaoqi Avatar answered Oct 12 '22 14:10

biaobiaoqi