Java ClassLoader in-depth explanation (GO)

Source: Internet
Author: User
Tags null null

When the JVM (Java Virtual machine) starts, it forms an initial ClassLoader hierarchy consisting of three classloader:

Bootstrap ClassLoader
|
Extension ClassLoader
|
System ClassLoader

Bootstrap ClassLoader-Boot (also known as primitive) class loader, which is responsible for loading Java's core classes. In Sun's JVM, you can specify additional classes by using the-xbootclasspath option in a command that executes Java or by using the-D option to specify Sun.boot.class.path system property values. This loader is very special, it is not actually a subclass of Java.lang.ClassLoader, but is implemented by the JVM itself. You can get bootstrap ClassLoader loaded with those core class libraries by executing the following code:
Url[] Urls=sun.misc.launcher.getbootstrapclasspath (). Geturls ();
for (int i = 0; i < urls.length; i++) {
System.out.println (Urls.toexternalform ());
}
The result on my computer is:
Files:/c:/j2sdk1.4.1_01/jre/lib/endorsed/dom.jar
Files:/c:/j2sdk1.4.1_01/jre/lib/endorsed/sax.jar
Files:/c:/j2sdk1.4.1_01/jre/lib/endorsed/xalan-2.3.1.jar
Files:/c:/j2sdk1.4.1_01/jre/lib/endorsed/xercesimpl-2.0.0.jar
Files:/c:/j2sdk1.4.1_01/jre/lib/endorsed/xml-apis.jar
Files:/c:/j2sdk1.4.1_01/jre/lib/endorsed/xsltc.jar
Files:/c:/j2sdk1.4.1_01/jre/lib/rt.jar
Files:/c:/j2sdk1.4.1_01/jre/lib/i18n.jar
Files:/c:/j2sdk1.4.1_01/jre/lib/sunrsasign.jar
Files:/c:/j2sdk1.4.1_01/jre/lib/jsse.jar
Files:/c:/j2sdk1.4.1_01/jre/lib/jce.jar
Files:/c:/j2sdk1.4.1_01/jre/lib/charsets.jar
Files:/c:/j2sdk1.4.1_01/jre/classes
Now you know why we don't need to specify these class libraries in the system Properties classpath, because the JVM automatically loads them when it starts.

Extension ClassLoader-the extension classloader, which is responsible for loading the class pack of the jar in the JRE's extended directory (Java_home/jre/lib/ext or specified by the Java.ext.dirs system properties). This provides a standard mechanism for introducing new functionality beyond the Java core class. Because the default extension directory is common to all JVMs that are launched from the same JRE, the jar class package that is placed in this directory is visible to all JVM and system ClassLoader. Calling the method on this instance getparent () always returns null NULL, because the bootloader bootstrap ClassLoader is not a true ClassLoader instance. So when you execute the following code:
System.out.println (System.getproperty ("Java.ext.dirs"));
ClassLoader Extensionclassloader=classloader.getsystemclassloader (). GetParent ();
System.out.println ("The parent of Extension ClassLoader:" +extensionclassloader.getparent ());
The result is:
C:/j2sdk1.4.1_01/jre/lib/ext
The parent of extension classloader:null
Extension ClassLoader is the parent of the system ClassLoader, and Bootstrap ClassLoader is the parent of extension ClassLoader, But it is not an actual classloader, so it is null.

System ClassLoader-Systems (also known as applications) ClassLoader, which is responsible for loading the-classpath or Java.class.path system Properties from command Java when the JVM is started or Classpath the jar class package and classpath specified by the operating system properties. The ClassLoader can always be found through the static method Classloader.getsystemclassloader (). If not specifically specified, any classloader that the user customizes will use the ClassLoader as its parent loader. Execute the following code to get:
System.out.println (System.getproperty ("Java.class.path"));
The output is the Classpath set by the user in the System properties.
The ClassLoader load class uses the overall responsibility of the delegation mechanism. The so-called overall responsibility, that is, when a classloader loading a class, the class depends on the and reference of all classes are also loaded by this classloader, unless explicitly using another ClassLoader loading The delegate mechanism is to look for the parent class loader (instead of super, which is not an inheritance relationship with the parent ClassLoader Class), only to find it from its own classpath when the parent cannot find it. In addition, the class loading also uses the cache mechanism, that is, if the cache saved this class directly return it, if not only to read from the file and converted to class, and stored in the cache, this is why we modified the class but must restart the JVM to take effect.


The process for each ClassLoader loading class is:
1. Check if this class is loaded (that is, if there is this class in the cache), if there is 8, if not to 2
2. If the parent ClassLoader does not exist (without the parent, the parent must be bootstrap ClassLoader), to 4
3. Request the parent ClassLoader to load, if successful to 8, unsuccessful to 5
4. Request that the JVM be loaded from bootstrap ClassLoader if successful to 8
5. Look for the class file (from the classpath associated with this classloader). If not found then to 7.
6. Load class from file to 8.
7. Throw classnotfoundexception.
8. Return to class.

In 5.6 of these steps, we can implement our own loading strategy by overwriting ClassLoader's Findclass method. Even overwrite the LoadClass method to implement your own onboarding process.

The order of the ClassLoader is:
First bootstrap ClassLoader, then extension classloader, and finally system ClassLoader. You will find that the more important the loaded class is on the front. The reason for this is that for security reasons, consider if System ClassLoader "personally" loads the consequences of a destructive "Java.lang.System" class. This mechanism guarantees that the user, even with one such class, adds it to the classpath, but it will never be loaded, because the class always loads with bootstrap ClassLoader. You can execute the following code:
System.out.println (System.class.getClassLoader ());
You will see that the result is null, which means that Java.lang.System is loaded by the bootstrap ClassLoader because Bootstrap ClassLoader is not a true ClassLoader instance, but is implemented by the JVM. , as has been said before.

Let's take a look at how the JVM is building the structure of the ClassLoader for us:
Sun.misc.Launcher, as the name implies, when you execute a Java command, the JVM will first load and initialize a Launcher with bootstrap ClassLoader, executing the code:
System.out.println ("The Launcher s ClassLoader is" +sun.misc.launcher.getlauncher (). GetClass (). getClassLoader ());
The result is:
The Launcher ' s classloader is null (because it is loaded with bootstrap ClassLoader, so class loader is null)
Launcher Initializes a good class loader structure based on the system and command settings, which the JVM uses to get extension ClassLoader and system classloader, and loads all the classes that need to be loaded, Finally executes the Java command specified by the class with the static Main method. Extension ClassLoader is actually an instance of the Sun.misc.launcher$extclassloader class, and system ClassLoader is actually sun.misc.launcher$ An instance of the Appclassloader class. and are all Java.net.URLClassLoader subclasses.

Let's take a look at some of the code for Launcher's initial process.

Launcher part of the code:
public class Launcher {
Public Launcher () {
Extclassloader Extclassloader;
try {
Initialize extension ClassLoader
Extclassloader = Extclassloader.getextclassloader ();
} catch (IOException IOException) {
throw new Internalerror ("Could not create extension class loader");
}
try {
Initialize system classloader,parent is extension classloader
Loader = Appclassloader.getappclassloader (Extclassloader);
} catch (IOException Ioexception1) {
throw new Internalerror ("Could not create application class loader");
}
Set the system ClassLoader to the context ClassLoader of the current thread (described later)
Thread.CurrentThread (). Setcontextclassloader (loader);
......
}
Public ClassLoader getClassLoader () {
Return to System ClassLoader
return loader;
}
}

Extension ClassLoader part of the code:
Static Class Launcher$extclassloader extends URLClassLoader {

public static Launcher$extclassloader Getextclassloader ()
Throws IOException
{
File afile[] = Getextdirs ();
Return (Launcher$extclassloader) accesscontroller.doprivileged (new launcher$1 (Afile));
}
private static file[] Getextdirs () {
Get System Properties "Java.ext.dirs"
String s = system.getproperty ("Java.ext.dirs");
File afile[];
if (s! = null) {
StringTokenizer StringTokenizer = new StringTokenizer (s, file.pathseparator);
int i = Stringtokenizer.counttokens ();
Afile = new File;
for (int j = 0; J < i; J + +)
AFILE[J] = new File (Stringtokenizer.nexttoken ());

} else {
Afile = new File[0];
}
return afile;
}
}

Partial code for System ClassLoader:
Static class Launcher$appclassloader extends URLClassLoader
{

public static ClassLoader Getappclassloader (ClassLoader ClassLoader)
Throws IOException
{
Get System Properties "Java.class.path"
String s = system.getproperty ("Java.class.path");
File afile[] = s! = null? LAUNCHER.ACCESS$200 (s): New file[0];
Return (Launcher$appclassloader) accesscontroller.doprivileged (new launcher$2 (S, Afile, ClassLoader));
}
}

Read the source code it's clear that extension ClassLoader uses the system property "Java.ext.dirs" to set the class search path, and there is no parent. System ClassLoader is a class search path that is set using the systems Properties "Java.class.path" and has a parent classloader. Launcher initializes the extension Classloader,system ClassLoader and sets the system ClassLoader to context ClassLoader, but returns only the system ClassLoader to the JVM.

How does this come out again a context ClassLoader? What's the use of it? When we build a thread, we can specify a suitable classloader as the context ClassLoader for this thread through the Setcontextclassloader method, and when this thread runs, We can get this context ClassLoader through the Getcontextclassloader method, and we can use it to load the class we need. The default is System ClassLoader. With this feature, we can "break" the ClassLoader delegation mechanism, and the parent ClassLoader can get the context ClassLoader of the current thread, and this context ClassLoader can be its child classloader or other ClassLoader, then the parent ClassLoader can obtain the desired Class from it, which breaks the limit that can only be requested by the parent ClassLoader. This mechanism can satisfy when our classpath is determined at runtime and loaded by a custom ClassLoader, the class loaded by the system ClassLoader (that is, in the JVM classpath) can be passed through the context ClassLoader gets custom ClassLoader and loads into a specific class (usually abstract classes and interfaces, which are implemented in custom ClassLoader), such as a servlet in a Web application that is loaded with this mechanism.


Well, now that we understand the structure and working principle of classloader, how do we implement dynamic loading and updating at runtime? As long as we can dynamically change the class search path and clear the ClassLoader cache has already loaded class, there are two scenarios, one is we inherit a ClassLoader, covering LoadClass method, Dynamically looking for class files and using the DefineClass method, the other is very simple and practical, just re-use a new class search path to new a classloader, so that the class search path to load the new class, also regenerate a A blank cache (of course, the class search path must not necessarily change). Oh, great, we hardly have to do any work, Java.neturlclassloader is a classloader! that meets our requirements. We can use it directly or inherit it!

This is a description of the two constructors of URLClassLoader in the doc of the j2se1.4 API:
URLClassLoader (url[] URLs)
Constructs a new urlclassloader for the specified URLs using the default delegation parent ClassLoader.
URLClassLoader (url[] URLs, ClassLoader parent)
Constructs a new urlclassloader for the given URLs.
where url[] URLs is the class search path we want to set, the parent is the ClassLoader parent ClassLoader, the default is the system ClassLoader.


OK, now we can load class dynamically, so we can use the Newinstance method to get an object. But how do we put this object in shape? Can this object be styled as its own class?

First let's analyze the Java source file compilation, run it! The Javac command is a compile method that calls "Com.sun.tools.javac.Main" in "Java_home/lib/tools.jar" to compile:

public static int Compile (String as[]);

public static int Compile (String as[], printwriter printwriter);

A return of 0 means that the compilation succeeds, and the string array as is the parameter we compile with the Javac command, separated by a space. For example:
Javac-classpath C:/foo/bar.jar;. -D c:/C:/some.java
The string array as {"-classpath", "C://foo//bar.jar;.", "-D", "c://", "C://some.java"}, if with the PrintWriter parameter, The compilation information is then given to the specified printwriter. The default output is System.err.

Where main is loaded by the JVM using launcher initialized system ClassLoader, the compiler will find all of the classes it relies on and referenced by the system when parsing the Java source file based on the overall responsibility principle. ClassLoader loading, if system ClassLoader cannot load a class, the compiler throws a "cannot resolve symbol" error.

So the first compile, that is, the compiler can not compile a reference to the unknown class is not in the Java source file, and because of the spelling error or classpath the required class library into the classpath, we must often see this "cannot resolve Symbol "This compilation error!"

Second, we put this class in the compilation path, successfully compiled, and then run it without putting it into classpath and using our own ClassLoader to dynamically load the class, this time also appears " Java.lang.NoClassDefFoundError "The violation, why?

Let us analyze again, the executable class that first calls this styling statement must be loaded by the JVM using the launcher initialized system ClassLoader, and the JVM will use the system when we do the styling, according to the overall responsibility principle. ClassLoader tries to load the class to sculpt the instance, and naturally throws a "java.lang.NoClassDefFoundError" violation when the system ClassLoader does not find the class.

OK, now let's summarize, Java file compilation and class loading execution, all using launcher initialized system ClassLoader as class loader, we can not dynamically change the system ClassLoader, It is also impossible for the JVM to use our own ClassLoader to replace system ClassLoader, which, under the overall responsibility principle, restricts compilation and runtime, and we cannot directly explicitly use a class that the system ClassLoader cannot find. That is, we can only use the Java Core Class library to extend class libraries and class libraries in classpath.

Not yet! To try this again, we put this class into the classpath so that system ClassLoader can recognize and load it. We then use our own ClassLoader to load this class from the specified class file (not being able to delegate the parent load, as this will be loaded by system ClassLoader from Classpath), then instantiate an object , and form this class, so that the JVM also recognizes this class (because the system ClassLoader can locate and load this class from the classpath), loading it is not the class in Classpath, but from Classpath outside the dynamic load, so the head of the bar! Unfortunately, there is a "java.lang.ClassCastException" violation.

Why is it? We also analyze, yes, although we use our own ClassLoader from outside the classpath to load this class dynamically, but the example of its model is that the JVM will use the system ClassLoader to load this class again, and try to sculpt an instance of class that uses our own ClassLoader loaded into the class (the other one) loaded by system ClassLoader. Have you found any problems? That is, we are trying to sculpt an instance of class from a classloader into another ClassLoader loaded class, even though the names of the two classes are loaded from the same class file. Unfortunately, the JVM thinks that this two class is different, that is, the JVM thinks different classloader load the same name of the class (even if it is loaded from the same class file) is different! The reason for this is that I think it's mostly for security reasons, so that all the core Java classes are loaded with system ClassLoader, and we can't replace their instances with instances of class of the same name that our ClassLoader load.

Seeing here, the smart reader must have thought of how to dynamically load our class, instantiate, shape and call it!

That is to take advantage of the polymorphism of one of the basic object-oriented features. Let's look at the example of a class that we dynamically load into a parent class that its system ClassLoader can recognize! What is this for? We'll have to analyze it again. When we use our own ClassLoader to dynamically load this, we just have to take this class and find that it has a parent class, and the JVM first loads the parent class before loading it, and the parent class is the system ClassLoader can be identified, according to the delegation mechanism, it will be loaded by the system ClassLoader, and then our ClassLoader loaded into this class, create an instance, the shape of the parent class, notice that the shape of the parent class Class (that is, upstream) is allowed by the object-oriented Java language and is supported by the JVM, the JVM uses system ClassLoader to load the parent class again, and then sculpt this instance into the parent class. As you can see from this process, the parent class is loaded by the system ClassLoader, which is the same class loader loaded with the same class, so there is no exception when styling. In the case of polymorphism, when calling the method of the parent class, it is true that the class (not the parent class) overrides the method of the parent class. These methods can also refer to the class that is not recognized by the system ClassLoader, because according to the overall responsibility principle, as long as the classloader that is loaded into this class is our own defined ClassLoader can locate and load these classes on the line.

This allows us to define a set of interfaces or base classes in advance and put them into classpath, and then dynamically load implementations or inherit the subclasses of those interfaces or base classes at execution time. Don't you get it? Let's think of the servlet, Web application server can load any class that inherits the servlet and execute them correctly, regardless of what the actual class is, or instantiate them as a servlet class, Then execute methods such as Init,dopost,doget and destroy of the servlet, regardless of whether the servlet is from Web-inf/lib and web-inf/classes under the system ClassLoader's sub-classloader (i.e. custom ClassLoader) is dynamically loaded. I have been told so much that we all understand. In containers such as APPLET,EJB, this mechanism is used.

For all the above situations, we hope that we can actually write some example to experiment.

Finally I say something else, ClassLoader, although called the ClassLoader, but does not mean that it can only be used to load class, we can also use it to get pictures, audio files and other resources such as the URL, of course, these resources must be in the classpath in the Jar class library or directory. Let's look at the two ways to find resources and class for ClassLoader in the doc for API:
Public URL getresource (String name)
To find a resource with the specified name, a resource is some data (pictures, audio, text, and so on) that can be accessed by the class code in a way that relies on the location of the code.
The name of a resource is the path name of the resource that is delimited with the '/' number.
This method will first request the parent ClassLoader to search for the resource, and if there is no parent, it will be searched in the path of the ClassLoader (that is, Bootstrap ClassLoader) built into the virtual machine. If it fails, this method calls FindResource (String) to find the resource.
public static URL Getsystemresource (String name)
Finds a resource of the specified name from the search path used to load the class. This method uses the System class loader to locate the resource. That is equivalent to Classloader.getsystemclassloader (). GetResource (name).

For example:
System.out.println (Classloader.getsystemresource ("Java/lang/string.class"));
The result is:
Jar: File:/c:/j2sdk1.4.1_01/jre/lib/rt.jar!/java/lang/string.class
Indicates that the String.class file is in the Java/lang directory of Rt.jar.
So we can package the pictures and other resources together with the class into the Jar class library (of course, can also package these resources separately) and add them to the search path of class loader, we can not care about the specific location of these resources, let class loader to help us find!

Java ClassLoader in-depth explanation (GO)

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.