Skip to content
Advertisement

Does the classloader load a class file if the corresponding class is never used?

To make my question more clear, consider following use-case:

Suppose there is a package that permits a set of operations on a given platform, for example a class to edit the registry on Windows. This package does not exist on other platforms, as there is no equivalent operation on other operating system.

For the sake of simplicity, consider

windows/Registry.java

package windows;

public class Registry {

  static Registry instance = null;
  static{
    System.out.println("print from static block");
  }

  private Registry() {
    System.out.println("Registry instance created!");
  }

  public static synchronized Registry getInstance() {
    if (null == instance) {
      instance = new Registry();
    }
    return instance;
  }

  public void foo() {
    System.out.println("foo called.");
  }
}

and the class where I am going to conditionally use the registry: main/Main.java

package main;

import windows.Registry;

public class Main {

  public static void test1(boolean onWindows) {
    if (onWindows) {
      Registry instance = Registry.getInstance();
      System.out.println("We are on Windows: ");
      instance.foo();
    } else {
      System.out.println("We are somewhere else!");
    }
  }

  public static void main(String[] args) {
    System.out.println("Entered main");
    boolean onWindows = args.length > 0 ? Boolean.parseBoolean(args[0]) : false;
    test1(onWindows);
  }
}

The question is, if no function or class is explicitly executed in Main.class, is it guaranteed that no code from Registry.class is executed?

I was able to test this example on multiple Desktop Platforms and with different java versions, but I would like to know if this behavior is documented and one can rely on it, if it thus the expected behavior also on other platforms, like android or embedded version of the JRE.

In case there is no such guarantee, as a (maybe custom) classloader might decide to load all .class files in the classpath, can I assume that the code will work without a java.lang.NoClassDefFoundError if onWindows is false and I remove Registry.class from the classpath?

The behavior I’ve observed so far is

rm -rf bld; mkdir bld && javac -d bld src/windows/Registry.java src/main/Main.java && java -classpath bld main.Main true
Entered main
print from static block
Registry instance created!
We are on Windows: 
foo called.

rm -rf bld; mkdir bld && javac -d bld src/windows/Registry.java src/main/Main.java && java -classpath bld main.Main false
Entered main
We are somewhere else!


rm -rf bld; mkdir bld && javac -d bld src/windows/Registry.java src/main/Main.java && rm -r bld/windows && java -classpath bld main.Main false
Entered main
We are somewhere else!

rm -rf bld; mkdir bld && javac -d bld src/windows/Registry.java src/main/Main.java && rm -r bld/windows && java -classpath bld main.Main true
Entered main
Exception in thread "main" java.lang.NoClassDefFoundError: windows/Registry
        at main.Main.test1(Main.java:9)
        at main.Main.main(Main.java:20)
Caused by: java.lang.ClassNotFoundException: windows.Registry
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
        ... 2 more

Are those behavior (lazy classloader and removing Registry.class) defined?

Advertisement

Answer

The question is, if no function or class is explicitly executed in Main.class, is it guaranteed that no code from Registry.class is executed?

That doesn’t touch directly on class loading, but rather on class initialization, which is the first point at which any code from the class in question gets executed. Specifically, static initialization blocks and the initializers of static members are executed during this stage. The class in question must already have been loaded and verified at this point, but it might have been loaded an arbitrary amount of time earlier.

Per JLS 12.4.1,

A class or interface T will be initialized immediately before the first occurrence of any one of the following:

  • T is a class and an instance of T is created.

  • A static method declared by T is invoked.

  • A static field declared by T is assigned.

  • A static field declared by T is used and the field is not a constant variable

Thus, if you never instantiate the class or access access any of its static methods or fields (except to read a static field that is a “constant variable”) then no code from the class will ever be executed.

But that a class does not get initialized does not mean that no attempt will be made to load it. The JLS does not forbid implementations from loading classes prospectively. In fact, JLS 12.2.1 specifically says:

a class loader may cache binary representations of classes and interfaces, prefetch them based on expected usage, or load a group of related classes together.

Thus, no, it is not safe to rely on the application represented by your class main.Main to run without a java.lang.NoClassDefFoundError or other loading error when class windows.Registry cannot be loaded, regardless of whether it can be expected actually to be used. However, you can, under the right circumstances, rely on that class not being initialized, and therefore no code from it being executed.

User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement