Java class loading
Author: binildas studas 01/26/2005
Translation: purplerain
Copyright Notice
: This statement can be reproduced at will. During reprinting, you must mark the original source and author information of the article as hyperlinks and this statement.
Author:
Binildas; purplerain
Address: http://www.onjava.com/pub/a/onjava/2005/01/26/classloading.html
Chinese address:
Http://www.matrix.org.cn/resource/article/43/43875_Class_Loading.html
Keywords: Java class loading
Class loading is one of the most powerful mechanisms provided by the Java language. Although class loading is not a hot topic, all programmers should understand its working mechanism and understand how to make it meet our needs.
Yes. This effectively saves our coding time and frees us from the work of continuously debugging classnotfoundexception and classcastexception.
This article starts from the basics, such as what is the difference between code and data, and how they form an instance or object. Then we will explore how Java Virtual Machine (JVM) can replace the Class Loader with read
Code, and the main types of class loaders in Java. Next, we will use a basic algorithm for class loading to see how the Class Loader loads an internal class. The next section of this article demonstrates a piece of code to show that expansion and development belong
The necessity of the class loader. Next I will explain how to use a custom class loader to complete a general task, so that it can load the code of any remote client, define in JVM, instantiate and execute
It. This article includes the J2EE specification for class loading-in fact, this has become one of the J2EE standards.
Class and Data
A class indicates the code to be executed, and data indicates its related status. The status changes frequently, but the Code does not. When we compare a specific state with a class, it means to make a class case.
Although instances of the same class have different statuses, they all correspond to the same code. In Java, a class usually has a. Class file, but there are exceptions. Running in Java
Environment (Java
Runtime), each class has a code that appears as a first-class Java object. It is an instance of Java. Lang. Class. Me
Compile a Java file. The compiler will embed a public, static,
The final modifier is of the Java. Lang. class type, and the domain variable named class is in its bytecode file. Because the public modifier is used, we can use the following form
Access:
java.lang.Class klass = Myclass.class;
Once a class is loaded into JVM, the same class will not be loaded again (Remember, the same class ). There is a problem here: what is "the same class "? Just as an object has a specific State, that is, identifier, an object is always associated with its code (class. Similarly, the classes loaded into JVM also have a specific Identifier. Let's take a look.
In Java, a class uses its fully matched Class Name (fully qualified class)
Name). Here, the fully-matched class name includes the package name and class name. However, in JVM, a class uses its full name and an instance that loads classloader as a unique identifier. Therefore, for example
If a package named PG contains a class named Cl, it is loaded by an instance kl1 of the Class Loader klassloader, And the CL instance, that is, c1.class is represented
(CL, PG, kl1 ). This means the instances (CL, PG, kl1) and (CL, PG,
Kl2) is different, so the classes loaded by them are completely different and incompatible. So how many types of loaders are there in the JVM? In the next section, we will reveal the answer.
Class Loader
In JVM, every class is. lang. some instances of classloader to load. class classloader is in the package Java. lang, developers can freely inherit it and add their own functions to load classes.
Whenever we type Java mymainclass to start running a new JVM
Loader) "is responsible for loading some key Java classes, such as Java. Lang. Object and other runtime code into the memory first. The runtime class is in JRE/lib
/RT. jar package file. Because this is the underlying execution action of the system, we cannot find the working details of the bootstrap loader in the Java documentation. For the same reason, the behavior of the bootstrap loader is
JVM is also quite different.
Similarly, if we use the following method:
log(java.lang.String.class.getClassLoader());
To obtain the loader of the core runtime class of Java, and then get null.
Next we will introduce the Java extension class loader. The extension Library provides more features than the Java running code. We can save the extension library in the path provided by the java. Ext. dirs attribute.
(Edit note: the java. Ext. dirs attribute refers to a key under the system attribute. All system attributes can be obtained through the system. getproperties () method.
. In the editor's system, the value of Java. Ext. dirs is "C:/Program
Files/Java/jdk1.5.0 _ 04/JRE/lib/EXT ". As mentioned below, java. Class. PATH also belongs to the same system attribute.
Key .)
The extclassloader class is used to load. Jar files under all java. Ext. dirs. Developers can add their own. Jar files or library files to the classpath of the extension directory so that they can be read by the extension class loader.
From the developer's point of view, the third and most important type of loader is appclassloader. This type of loader is used to read all classes corresponding to the path of the Java. Class. Path System attribute.
In Sun's Java guide, the article "Understanding extension class loading" (understanding extension class loading) provides a more detailed explanation of the paths of the above three class loaders, this is the classloader of several other JDK versions.
● Java.net. urlclassloader
● Java. Security. secureclassloader
● Java. RMI. server. rmiclassloader
● Sun. Applet. appletclassloader
Java. Lang. Thread, including public classloader
Getcontextclassloader () method, which returns the context class loader for a specific thread. This type of loader is provided by the thread Creator for running in this thread
The line of code is used when classes or resources need to be loaded. If this loader is not created, it is the context class loader of its parent thread by default. The original Class Loader is generally created by the class loader that reads the application program.
How does the Class Loader Work?
In addition to the bootstrap loader, all classloaders have a parent loader. In addition, all classloaders are of the Java. Lang. classloader type. Load the above two types
It is also important for the normal operation of the Class Loader customized by the developer. The most important aspect is to correctly set the parent class loader. In any class loader, its parent class loader is the class that loads the class loader.
Loader instance. (Remember, the Class Loader itself is also a class !)
The loadclass () method can be used to obtain the class from the class loader. We can use the source code of Java. Lang. classloader to understand the details of this method, as follows:
protected synchronized Class<?> loadClass
(String name, boolean resolve)
throws ClassNotFoundException{
// First check if the class is already loaded
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClass0(name);
}
} catch (ClassNotFoundException e) {
// If still not found, then invoke
// findClass to find the class.
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
We can use the classloader's two constructor methods to set the parent Class Loader:
public class MyClassLoader extends ClassLoader{
public MyClassLoader(){
super(MyClassLoader.class.getClassLoader());
}
}
Or
public class MyClassLoader extends ClassLoader{
public MyClassLoader(){
super(getClass().getClassLoader());
}
}
The first method is more commonly used, because it is generally not recommended to call the getclass () method in the constructor, because the initialization of the object is completed only at the exit of the constructor. Therefore, if the parent class is loaded
Is correctly created. To obtain a class from an instance of a class loader, if it cannot find this class, it should first access its parent class. If the parent class cannot be found (that is, the parent class cannot be found)
Class, etc.), and if the findbootstrapclass0 () method also fails, the findclass () method is called. Default findclass () method
Classnotfoundexception will be thrown. When they inherit java. Lang. classloader to customize the class loader, developers need to implement this
Method. The default Implementation of findclass () is as follows:
protected Class<?> findClass(String name)
throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
Within the findclass () method, the Class Loader needs to obtain bytecode from any source. The source can be a file system, URL, database, another application that can generate bytecode, and other classes.
Can generate the source of java standard bytecode. You can even use bcel (byte code Engineering
Library: bytecode Engineering Library), which provides shortcuts for creating classes at runtime. Bcel has been successfully used in the following aspects: compiler, optimizer, obfuscator, code generator and other analysts
. Once the bytecode is retrieved, this method calls the defineclass () method, which varies with the loading instances of different classes. Therefore, if the two classes load the instance definition from the same source
The results of a class are different.
Java language specification (specification) describes in detail the loading, linking, or initialization processes of classes or interfaces in the Java execution engine.
Figure 1 shows an application called mymainclass. According to the previous descriptions, mymainclass. class will be loaded by appclassloader.
Mymainclass creates two class loaders: customclassloader1 and
Customclassloader2, which can obtain the bytecode named target from a data source (such as a network. This indicates that the class definition of the target class is not in the application class path.
Or extend the class path. In this case, if mymainclass wants to use a custom class loader to load the target class, customclassloader1 and
Customclassloader2 loads and defines the target. Class class independently. This is of great significance in Java. If the target class has some static
Initialize the code, and assume that we only want the code to be executed once in JVM, and the code will be executed twice in our current step, which is different from
Customclassloaders load and execute. If the class target is loaded by two mmclassloaders and two instances are created: target1 and
TARGET2, a display, they are not compatible with the type. In other words, the following code cannot be executed in JVM:
Target target3 = (Target) target2;
The above code throws a classcastexception. This is because JVM treats them as different classes because they are defined by different class loaders. In this case
Use two different class loaders: mmclassloader1 and
Customclassloader2, the same error occurs when you use different instances of the same class loader customclassloader. These will be later in this article
Use specific code.
Figure 1. Load the same target class to multiple class loaders in the same JVM
For more information about class loading, definitions, and links, see Andreas Schaefer's "inside class loaders ."
Why do we need our own class loaders?
One of the reasons is that developers write their own class loaders to control class loading behaviors in JVM. Classes in Java are identified by their package names and class names.
Java. Io. serializable interface class, serialversionuid plays an important role in identifying the class version. This unique identifier is a class name and Interface
A 64-bit hash Field Composed of names, member methods, and attributes, and there is no other quick way to identify the version of a class. Strictly speaking, if all of the above matches, it belongs to the same class.
But let's think about the following situation: we need to develop a general execution engine. You can execute any task that implements a specific interface. When a task is submitted to this engine, the code of the task needs to be loaded first.
Assuming that different customers submit different tasks for this engine, coincidentally, all these tasks have the same class name and package name. The question is whether the engine can send messages to different users.
And make different responses. The following sections provide sample code for downloading: samepath and
Differentversions. These two directories demonstrate this concept respectively.
Figure 2 shows the file directory structure. There are three subdirectories samepath, differentversions, and differentversionspush. The example is as follows:
Figure 2. folder structure organization example
In samepath, the class version. version is stored in the V1 and V2 subdirectories. The two classes have the same class name and package name. The only difference is the following line:
public void fx(){
log("this = " + this + "; Version.fx(1).");
}
In V1, the log records include version. FX (1), while in V2 it is version. FX (2 ). Place the two classes with slight differences under a classpath and run the test class:
Set classpath =.; % current_root %/V1; % current_root %/v2
% Java_home %/bin/Java Test
Figure 3 shows the console output. We can see that the code corresponding to version. FX (1) is executed, because the Class Loader first sees the code of this version in classpath.
Figure 3. samepath test first in the class path version 1
Run the program again and make the following minor changes to the class path.
Set classpath =.; % current_root %/V2; % current_root %/V1
% Java_home %/bin/Java Test
Figure 4 shows the output of the console. The code corresponding to version. FX (2) is loaded, because the Class Loader first finds its path in classpath.
Figure 4. samepath test version 2 at the top of the class path
According to the above example, it is obvious that the class loader loads the elements first found in the class path. If we delete version. version in V1 and V2
A. jar file in version. Version format, such as myextension. jar, put it in the corresponding java. Ext. dirs path. Run it again and check it.
Version. version is not loaded by appclassloader, but by the extension class loader. 5.
Figure 5. appclassloader and extclassloader
In this example, the folder differentversions contains an Rmi execution engine, which can be provided to the execution engine by the client to implement common. taskintf
Interface task. The sub-folders Client1 and Client2 contain the client. taskimpl class which have two slightly different versions. The differences between the two classes are as follows:
static{
log("client.TaskImpl.class.getClassLoader
(v1) : " + TaskImpl.class.getClassLoader());
}
public void execute(){
log("this = " + this + "; execute(1)");
}
In Client1 and Client2, getclassloader (V1), execute (1), getclassloader (V2), and
Execute (2) log statement. In addition, in the Code of the execution engine RMI server, we randomly put the Client2 task implementation in front of the class path.
Classpath = % current_root %/common; % current_root %/server;
% Current_root %/Client2; % current_root %/Client1
% Java_home %/bin/Java Server. Server
6, 7, 8 screens, on the client Vm, their respective client. taskimpl classes are loaded, instantiated, and sent to the server Vm for execution. From the server console, you can
Obviously, the client. taskimpl code is executed only once by the VM on the server side. This single code version is used by many instances on the server side and executes tasks.
Figure 6. execution engine server console
Figure 6 shows the console of the server, loading and executing two different client requests, as shown in 7 and 8. Note that the code is loaded only once (as can be seen from the log of the static initialization block), but this method is executed twice for client calls.
Figure 7. execution engine client 1 Console
In Figure 7, the client VM loads the taskimpl code containing the log Content of client. taskimpl. Class. getclassloader (V1) and provides it to the server's execution engine. Figure 8 the client VM loads another taskimpl code and sends it to the server.
Figure 8. execution engine client 2 Console
In the VM of the client, client. taskimpl is loaded, initialized, and sent to the server for execution. Figure 6 also reveals that the client. taskimpl code is only in the server
The VM at the service end is loaded once, but this "unique once" creates many instances on the server and executes them. Maybe client 1 should be unhappy because it is not
The client. taskimpl (V1) method call is executed by the server, but some other code. How can this problem be solved? The answer is to implement a custom class loader.
Custom Class Loader
To better control the loading of classes, you must implement a custom class loader. All custom class loaders should inherit from Java. Lang. classloader. In the constructor, we also
Set the parent class loader. Then rewrite the findclass () method. The differentversionspush folder contains a file named
The custom class loader of filesystemclassloader. Its structure 9 is shown in.
Figure 9. Custom Class Loader relationship
The main method implemented in common. filesystemclassloader is as follows:
public byte[] findClassBytes(String className){
try{
String pathName = currentRoot +
File.separatorChar + className.
replace('.', File.separatorChar)
+ ".class";
FileInputStream inFile = new
FileInputStream(pathName);
byte[] classBytes = new
byte[inFile.available()];
inFile.read(classBytes);
return classBytes;
}
catch (java.io.IOException ioEx){
return null;
}
}
public Class findClass(String name)throws
ClassNotFoundException{
byte[] classBytes = findClassBytes(name);
if (classBytes==null){
throw new ClassNotFoundException();
}
else{
return defineClass(name, classBytes,
0, classBytes.length);
}
}
public Class findClass(String name, byte[]
classBytes)throws ClassNotFoundException{
if (classBytes==null){
throw new ClassNotFoundException(
"(classBytes==null)");
}
else{
return defineClass(name, classBytes,
0, classBytes.length);
}
}
public void execute(String codeName,
byte[] code){
Class klass = null;
try{
klass = findClass(codeName, code);
TaskIntf task = (TaskIntf)
klass.newInstance();
task.execute();
}
catch(Exception exception){
exception.printStackTrace();
}
}
This class is used by the client to convert client. taskimpl (V1) to a byte array, which is then sent to the RMI server. On the server side, the same class is used to convert the content of the byte array back to the code. The client code is as follows:
public class Client{
public static void main (String[] args){
try{
byte[] code = getClassDefinition
("client.TaskImpl");
serverIntf.execute("client.TaskImpl",
code);
}
catch(RemoteException remoteException){
remoteException.printStackTrace();
}
}
private static byte[] getClassDefinition
(String codeName){
String userDir = System.getProperties().
getProperty("BytePath");
FileSystemClassLoader fscl1 = null;
try{
fscl1 = new FileSystemClassLoader
(userDir);
}
catch(FileNotFoundException
fileNotFoundException){
fileNotFoundException.printStackTrace();
}
return fscl1.findClassBytes(codeName);
}
}
In the execution engine, the Code received from the client is sent to the custom class loader. The custom Class Loader defines it as a class from the byte array, instantiate and execute it. It should be noted that, for every customer request, we
Use different instances of the filesystemclassloader class to define the client. taskimpl submitted by the client. In addition, client. taskimpl and
It is not in the server class path. This means that when we call the findclass () method in filesystemclassloader, findclass () calls
The internal defineclass () method. Class client. taskimpl is defined by a specific class loader instance. Therefore, when
A new instance of filesystemclassloader is used, and the class is redefined as a byte array. Therefore, for each client request class client. taskimpl
After being defined multiple times, we can execute different client. taskimpl codes in the same execution engine JVM.
public void execute(String codeName, byte[] code)throws RemoteException{
FileSystemClassLoader fileSystemClassLoader = null;
try{
fileSystemClassLoader = new FileSystemClassLoader();
fileSystemClassLoader.execute(codeName, code);
}
catch(Exception exception){
throw new RemoteException(exception.getMessage());
}
}
The example is in the differentversionspush folder. The console interfaces of the server and client are as follows:
Figure 10. Custom Class Loader execution engine
Figure 10 shows the custom classloader console. We can see that the client. taskimpl code is loaded multiple times. Classes are loaded and initialized for each client.
Figure 11. Custom class loader, client 1
In Figure 11, the taskimpl-class code containing the log records of client. taskimpl. Class. getclassloader (V1) is
Load and then send it to the server. Figure 12
Another client loads the class code containing client. taskimpl. Class. getclassloader (V1) and sends it to the server.
Figure 12. Custom class loader, client 1
This Code demonstrates how to use different classloader instances to execute different versions of code on the same VM.
J2EE Class Loader
The J2EE server tends to discard the original class and reload the new class at a certain interval. In some cases, it is executed in this way, but in some cases it is not. Similarly, if a web server is to be discarded
A servlet instance may be manually operated by the server administrator, or it may be that the instance has not been matched for a long time. When a JSP page is requested for the first time, the container translates the JSP page into
Servlet code in a specific form. Once the servlet code is created, the container will translate the servlet into a class file for use. For each request submitted to the container
The container first checks whether the JSP file has been modified. If yes, re-translate the file to ensure that every request is updated in a timely manner. Enterprise-level deployment solutions use. Ear,
. War,
Files in the form of. rar must also be reloaded, which may be random or executed regularly according to certain configuration schemes. For all of these cases-loading, detaching, and reloading classes ...... All are created
It is based on the class loading mechanism we control the application server. To implement these class loaders that need to be extended, it can execute classes defined by itself. Brett
Peterson has already written in his article understanding J2EE application server class loading
Ubuntures provides a detailed description of the class loading solution of the J2EE Application Server. For details, see theserverside.com.
Close
This article discusses how a class is uniquely identified when loaded to a virtual machine, and issues that occur when the class has the same class name and package name. Because there is no directly available class version management mechanism, if we want
When loading a class as needed, you need to customize the class loader to expand its behavior. We can use the "hot deployment" feature provided by many J2EE servers to reload a new version of the class without changing it.
The VM of the server. Even if the application server is not involved, we can use a custom class loader to control the specific behavior of Java applications when loading classes. Ted
Neward's book server-based Java programming details Java class loading, J2EE APIs, and the best way to use them.
Reference
● Source code of this article:
Download file
● JDK 1.5 API documentation
● Java Language Specification
● "Understanding extension class loading" in Java tutorial"
● "Inside class loaders" in onjava"
● In onjava, "inside class loaders: Debugging"
● In javaworld, "What version is your Java code? "
● "Understanding J2EE application server class loading ubuntures" in theserverside"
● Bytecode Engineering Library
● Server-based Java programming, Author: Ted neward
Binildas certified studas is a senior technical architect in Infosys's communication service providers practice (CSP) and Sun Certified Enterprise architect and Microsoft certified expert.