Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does "new" do in Java w.r.t. class loader?

Tags:

I cannot easily find it in JLS/JVMSpec, nor in SO. I'm sure it must've been asked...

So, what does "new" do actually? Suppose we instantiate a class B in A:

class A {
    // ...
    new B();
    // ...
}

Is this equivalent to

class A {
    // ...
    A.class.getClassLoader().loadClass("B's canonical name").newInstance();
    // ...
}

?

Does it, or does it not work like that in every environment?

I'd be grateful if you can point me to the appropriate chapter in JLS/JVMSpec. Thanks!

Edit: surely we can't call B.class.getCanonicalName() in loadClass() call, since B's not loaded yet. JVM has to resolve the name based on the import statement.

like image 792
MaDa Avatar asked Oct 10 '11 08:10

MaDa


People also ask

What does class loader do in Java?

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.

Why do we need custom Classloaders in Java?

Here are the advantages of the custom class loader: Provides Modular architecture Allows to define multiple class loader allowing modular architecture. Avoiding conflicts Clearly defines the scope of the class to within the class loader.

Can we load the same class by two ClassLoader?

So when a class is loaded into JVM, you have an entry as (package, classname, classloader). Therefore the same class can be loaded twice by two different ClassLoader instances.


2 Answers

Is this equivalent to

class A {
    // ...
    A.class.getClassLoader().loadClass("B's canonical name").newInstance();
    // ...
}

?

No, not always.

Class loading is performed only once for a given namespace, unless the Class in question has been previously unloaded. Therefore, the equivalent expression A.class.getClassLoader().loadClass("B's canonical name") will be executed only once in most scenarios. In other words, if you have two expressions - new A(), the loadClass will be performed only once.

Invocation of a constructor is treated as a method invocation by the JVM, but this requires the cooperation of a Java compiler. The JVM and the compiler have to adhere to section 3.9 of the Java Virtual Machine Specification, which states:

3.9 Specially Named Initialization Methods

At the level of the Java virtual machine, every constructor (§2.12) appears as an instance initialization method that has the special name <init>. This name is supplied by a compiler. Because the name <init> is not a valid identifier, it cannot be used directly in a program written in the Java programming language. Instance initialization methods may be invoked only within the Java virtual machine by the invokespecial instruction, and they may be invoked only on uninitialized class instances. An instance initialization method takes on the access permissions (§2.7.4) of the constructor from which it was derived.

A class or interface has at most one class or interface initialization method and is initialized (§2.17.4) by invoking that method. The initialization method of a class or interface is static and takes no arguments. It has the special name <clinit>. This name is supplied by a compiler. Because the name <clinit> is not a valid identifier, it cannot be used directly in a program written in the Java programming language. Class and interface initialization methods are invoked implicitly by the Java virtual machine; they are never invoked directly from any Java virtual machine instruction, but are invoked only indirectly as part of the class initialization process.

This section assumes that the Class object pertaining to the class in question is available to the current thread. Once the Class object is available, the method <init> corresponding to the constructor with the right set of arguments, will be invoked.

The question of which classloader will be used to load the class, if not already loaded, is a bit different, and has nothing to do with the new keyword. It depends on how one class references another i.e. does a symbolic reference need to be resolved in the runtime constant pool? The behavior in this context is defined in Section 5.3 of the Java Virtual Machine Specification:

5.3 Creation and Loading

Creation of a class or interface C denoted by the name N consists of the construction in the method area of the Java virtual machine (§3.5.4) of an implementation-specific internal representation of C. Class or interface creation is triggered by another class or interface D, which references C through its runtime constant pool.

...

The Java virtual machine uses one of three procedures to create class or interface C denoted by N:

  • If N denotes a nonarray class or an interface, one of the two following methods is used to load and thereby create C :

    • If D was defined by the bootstrap class loader, then the bootstrap class loader initiates loading of C (§5.3.1).

    • If D was defined by a user-defined class loader, then that same user-defined class loader initiates loading of C (§5.3.2).

  • Otherwise N denotes an array class. An array class is created directly by the Java virtual machine (§5.3.3), not by a class loader. However, the defining class loader of D is used in the process of creating array class C.

Note the sentence - If D was defined by a user-defined class loader, then that same user-defined class loader initiates loading of C in the above quote. In the context of the expression new A(), the classloader that loaded the enclosing class will be responsible for loading A in accordance with the VM Spec; this is of course, assuming that the enclosing class is not loaded by the bootstrap classloader.

like image 194
Vineet Reynolds Avatar answered Sep 22 '22 17:09

Vineet Reynolds


To follow up on my comment, a line like

new A()

translates to

0:  new #2; //class A
3:  dup
4:  invokespecial   #3; //Method A."<init>":()V
7:  pop

And the stacktrace is:

  [1] java.net.URLClassLoader$1.run (URLClassLoader.java:202)
  [2] java.security.AccessController.doPrivileged (native method)
  [3] java.net.URLClassLoader.findClass (URLClassLoader.java:190)
  [4] sun.misc.Launcher$ExtClassLoader.findClass (Launcher.java:229)
  [5] java.lang.ClassLoader.loadClass (ClassLoader.java:307)
  [6] java.lang.ClassLoader.loadClass (ClassLoader.java:296)
  [7] sun.misc.Launcher$AppClassLoader.loadClass (Launcher.java:301)
  [8] java.lang.ClassLoader.loadClass (ClassLoader.java:248)
  [9] Loader.main (Loader.java:11)

So I guess you were pretty close in your guess.

like image 36
Miserable Variable Avatar answered Sep 19 '22 17:09

Miserable Variable