GitHub github.com/wangzhiwubi…
What is class loading
Loading a class involves reading binary data from a class’s.class file into memory, placing it in the method area of the runtime data area, and then creating a java.lang. class object in the heap that encapsulates the class’s data structure in the method area. The end product of Class loading is the Class object in the heap, which encapsulates the Class’s data structure in the method area and provides the Java programmer with an interface to access the data structure in the method area.
Instead of waiting for a class to be “first actively used” before loading it, the JVM specification allows the classloader to preload a class in anticipation of it being used, and if it encounters a missing.class file or an error during preloading, The class loader must not report a LinkageError until the program first actively uses the class. If the class is never actively used by the program, the class loader will not report an error
- How to load a. Class file
- Load. Class files downloaded over the network directly from your local system
- Load. Class files from zip, JAR, etc archives
- Extracting. Class files from a proprietary database dynamically compiles Java source files to. Class files
The process of class loading includes five stages: loading, verification, preparation, parsing and initialization. Of the five phases, the loading, validation, preparation, and initialization phases occur in a certain order, while the parsing phase does not, and in some cases can begin after the initialization phase, in order to support runtime binding (also known as dynamic binding or late binding) in the Java language. Also note that the phases here start in sequence, not proceed or complete sequentially, as these phases are often intermixed with each other, often invoking or activating one phase while the other is executing.
loading
Finding and loading binary data for a class Loading is the first phase of the class loading process, during which the virtual machine needs to do three things:
- Gets the binary byte stream defined by the fully qualified name of a class.
- Transform the static storage structure represented by this byte stream into the runtime data structure of the method area.
- A Java.lang. Class object representing this Class is generated in the Java heap as an access point to the data in the method area.
Compared to other phases of class loading, the loading phase (specifically, the action of the loading phase to retrieve the binary byte stream of the class) is the most controllable, because developers can either use the system-provided class loader to complete the loading, or they can customize their own. After the loading phase is complete, the binary byte streams outside the virtual machine are stored in the method area in the format required by the virtual machine, and an object of java.lang.Class is created in the Java heap through which the data in the method area can be accessed.
The connection
Validation: Verifying the correctness of the loaded classes is the first step in the connection phase. The purpose of this phase is to ensure that the byte stream of the Class file contains information that meets the requirements of the current virtual machine and does not compromise the security of the virtual machine. In the verification stage, four stages of inspection actions will be roughly completed:
- File format verification: verify whether the byte stream conforms to the Class file format specification; For example, whether the value starts with 0xCAFEBABE, whether the major and minor versions are within the processing range of the current VM, and whether the constants in the constant pool have unsupported types.
- Metadata verification: Semantic analysis of the information described by bytecode (note: compared with the semantic analysis in the compilation phase of JavAC) to ensure that the information described conforms to the requirements of the Java language specification; For example, does this class have a parent other than java.lang.object?
- Bytecode validation: Determine that program semantics are legitimate and logical through data flow and control flow analysis.
- Symbol reference validation: Ensures that the parse action is performed correctly. The validation phase is important, but not required, and has no effect on program runtime. If the referenced classes are repeatedly validated, consider using the -xVerifyNone parameter to turn off most of the class validation to shorten the virtual machine class load time.
Preparation: Allocates memory for static variables of a class and initializes them to default values. The preparation phase is the phase that formally allocates memory for class variables and sets their initial values, all of which will be allocated in the method area. There are several points to note about this phase:
- Static class variables are allocated, not instance variables, which are allocated in the Java heap along with the object when it is instantiated.
- 2. The initial value set here is usually the default zero value of the data type (such as 0, 0L, NULL, false, etc.) rather than the value explicitly assigned in Java code. Suppose a class variable is defined as: PublicStaticIntValue =3;
The initial value of value after the preparation phase is 0, not 3, because no Java methods have been executed yet, and the publicstatic instruction that assigns value to 3 is stored in the class constructor <clinit> () method after the program is compiled. Therefore, assigning value to 3 will only be performed during initialization. Here are some other points to note:
For basic data types, class variables (static) and global variables are assigned a default value of zero if they are used without an explicit assignment, while local variables must be explicitly assigned before they are used, otherwise they will fail at compile time. Constants that are both static and final must be explicitly assigned at declaration, otherwise they will not pass compilation. A constant that is only final can be explicitly assigned at declaration time or at class initialization. In general, it must be explicitly assigned before use and will not be given a default zero value. For reference data types, such as group references and object references, if they are directly used without explicit assignment, the system will give them the default zero value, namely NULL. If you do not assign values to the elements of the array when the array is initialized, the elements will be assigned a default value of zero based on the corresponding data type.
3. If a class field has a ConstantValue property in the field property table, which is both final and static, then the value variable is initialized to the value specified by the ConstValue property in the preparation phase. Assuming the above class variable value is defined as: publicstaticfinalintvalue = 3;
At compile time Javac will generate a ConstantValue attribute for value, and in preparation the vm will assign value to 3 based on the ConstantValue setting. Static final constants put their results into the constant pool of the calling class at compile time
Parsing: Converts symbolic references in a class to direct references
In the parsing phase, the VIRTUAL machine replaces symbolic references in the constant pool with direct references. The parsing action is mainly performed for class or interface references, fields, class methods, interface methods, method types, method handles, and call point qualifiers. A symbolic reference is a set of symbols that describe a target, which can be any literal. A direct reference is a pointer to a target directly, a relative offset, or a handle to the target indirectly.
Initialize the
Class initialization, which assigns correct initial values to static variables of a class. The JVM is responsible for initializing classes, mainly class variables. There are two ways to initialize a class variable in Java:
- ① Declare class variables to be specified initial values
- ② Use static code blocks to specify initial values for class variables
JVM initialization steps
- 1. If the class has not already been loaded and connected, the program loads and connects the class first
- 2. If the immediate parent of the class has not already been initialized, initialize its immediate parent first
- 3. If there are initializers in a class, the system executes those initializers in turn
Class initialization timing: Class initialization occurs only when a class is actively used. Active use of a class includes the following six types:
- Create an instance of the class, which is new
- Accesses or assigns a value to a static variable of a class or interface
- Call a static method of a class
- Reflection (e.g. Class.forname (” com.shengsiyuan.test “))
- If you initialize a subclass of a class, its parent class is initialized as well
- A class (JavaTest) that is marked as a startup class when the Java virtual machine starts, using the java.exe command directly to run a main class
End of life cycle
- The system.exit () method is executed
- The program completes normally
- The program encounters an exception or error during execution and terminates abnormally
- The Java virtual machine process was terminated due to an operating system error. Procedure
Class loaders
Looking for class loaders, let’s start with a small example
package com.neo.classloader; public class ClassLoaderTest { public static void main(String[] args) { ClassLoader loader = Thread.currentThread().getContextClassLoader(); System.out.println(loader); System.out.println(loader.getParent()); System.out.println(loader.getParent().getParent()); }}Copy the code
After running, the output is as follows:
sun.misc.Launcher$AppClassLoader@64fef26a
sun.misc.Launcher$ExtClassLoader@1ddd40f3
null
Copy the code
The Bootstrap Loader is implemented in C language. There is no definite way to return the parent Loader, so null is returned.
The hierarchy of these loaders is shown in the figure below:
Note: The parent class loader is not implemented by inheritance, but by composition.
From the perspective of Java virtual machines, there are only two different class loaders: boot class loaders, which are implemented in C++ (this is limited to Hotspot, the default virtual machine after JDK1.5, many other virtual machines are implemented in Java) and are part of the virtual machine itself; All other classloaders: these classloaders are implemented in the Java language, independent of the virtual machine, and all inherit from the abstract java.lang.ClassLoader class, which needs to be loaded into memory by the launcher ClassLoader before it can load other classes.
From a Java developer’s point of view, class loaders can be broadly divided into three categories:
Start the classloader: The Bootstrap ClassLoader is responsible for loading libraries that are stored in JDK\jre\lib(JDK stands for JDK installation directory, the same below) or in the path specified by -xbootclasspath and recognized by the virtual machine (such as rt.jar, All classes starting with Java.* are loaded by Bootstrap ClassLoader. Startup class loaders cannot be directly referenced by Java programs.
Extended class loaders: Launcher$ExtClassLoader, which is used to load the DK\jre\lib\ext directory. Or all libraries in the path specified by the java.ext.dirs system variable (such as classes beginning with Javax.*) can be used directly by the developer.
Application class loaders: The Application ClassLoader is implemented by sun.misc.Launcher$AppClassLoader, which loads the classes specified by the user’s ClassPath. Developers can use the Application ClassLoader directly. If the application does not have its own custom class loader, this is generally the default class loader in the application.
All three types of loaders work together to load applications, and custom class loaders can be added if necessary. Since the JVM’s built-in ClassLoader only knows how to load standard Java class files from the local file system, if you write your own ClassLoader, you can do the following:
1) Automatically validate the digital signature before executing the untrusted code.
2) Dynamically create custom build classes that meet the specific needs of users.
3) Retrieve Java classes from specific locations, such as databases and networks.
JVM class loading mechanism
• Full responsibility. When a Class loader is responsible for loading a Class, other classes that that Class depends on and references are also loaded by the Class loader, unless it is shown to be loaded using another Class loader
• Parent delegate, which first lets the parent loader try to load the class, and only tries to load the class from its own classpath if the parent loader is unable to load the class
• Cache mechanism, the cache mechanism will ensure that all loaded classes will be cached. When a program needs to use a Class, the Class loader first looks for the Class in the cache. Only when the cache does not exist, the system will read the binary data corresponding to the Class, convert it into a Class object, and store it in the cache. Is that why you have to restart the JVM to make Class changes to take effect
4. Class loading
There are three ways to load a class:
1. The command line startup application is initialized by the JVM
2. Load dynamically through class.forname ()
3. Use the classloader.loadClass () method to load dynamically
Example:
package com.neo.classloader; public class loaderTest { public static void main(String[] args) throws ClassNotFoundException { ClassLoader loader = HelloWorld.class.getClassLoader(); System.out.println(loader); // Use classloader.loadClass () to load classes without executing the initialization block loader.loadClass()"Test2"); // Use class.forname () to load the Class. By default, the initialization block is executed."Test2"); // Use class.forname () to load the Class and specify the ClassLoader, which does not execute static blocks when initialized."Test2".false, loader); }}Copy the code
Demo class
public class Test2 {
static {
System.out.println("Static initialization block executed!"); }}Copy the code
If you switch the loading mode, different output results are displayed.
Class.forname () distinguishes classloader.loadClass () from classloader.loadClass (). Classloader.loadclass () : does only one thing: loads a. Class file into the JVM. Static blocks are executed only at newInstance. Class. ForName (name, initialize, loader) also controls whether static blocks are loaded. And only the newInstance() method is invoked to create objects of the class using the call constructor.
5. Parental delegation model
The workflow of the parental delegation model is as follows: If a class loader received the request of the class loading, it won’t try to load the first class, but give parent to complete the request, in turn up, as a result, all of the class loading request should be passed to the top finally start the class loader, only when the parent loader in the scope of its search did not find the required class, is unable to complete the loading, The child loader will try to load the class itself.
When an AppClassLoader loads a class, it does not attempt to load the class itself. Instead, it delegates the request to the parent class loader, ExtClassLoader.
2. When ExtClassLoader loads a class, it does not attempt to load the class itself in the first place. Instead, it delegates the class loading request to BootStrapClassLoader.
3. If the BootStrapClassLoader fails to load (for example, the class cannot be found in $JAVA_HOME/jre/lib), the ExtClassLoader will be used to try loading.
4. If the ExtClassLoader also fails to load, AppClassLoader will be used to load the ExtClassLoader. If the AppClassLoader also fails to load, ClassNotFoundException will be reported.
public Class<? > loadClass(String name)throws ClassNotFoundException {return loadClass(name, false); } protected synchronized Class<? > loadClass(String name, Boolean resolve)throws ClassNotFoundException {// First check whether the type has been loaded Class c = findLoadedClass(name);if(c == null) {// If not loaded, delegate to the parent or delegate to the start class loader to load try {if(parent ! C = parent. LoadClass (name, parent) {// If there is a parent class loader, delegate to the parent class loader.false);
} elseFindBootstrapClass (String name) c = findBootstrapClass0(name); // If there is no parent Class loader, check whether the Class was loaded by the bootloader. }} catch (ClassNotFoundException e) {c = findClass(name);}} Catch (ClassNotFoundException e) {c = findClass(name); }}if (resolve) {
resolveClass(c);
}
return c;
}
Copy the code
Significance of parental delegation model:
– The system class prevents multiple copies of the same bytecode from appearing in memory
– Ensure the secure and stable running of Java programs
6. Custom class loaders
Normally, we use the system class loader directly. However, there are times when custom class loaders are also needed. For example, an application transmits Java class bytecodes over the network. To ensure security, the bytecodes are encrypted. In this case, the system class loader cannot load the bytecodes. Custom classloaders are generally inherited from classloaders, so we just need to override the findClass method. Here’s an example to illustrate the process of customizing a class loader:
package com.neo.classloader; import java.io.*; public class MyClassLoader extends ClassLoader { private String root; protected Class<? > findClass(String name) throws ClassNotFoundException { byte[] classData = loadClassData(name);if (classData == null) {
throw new ClassNotFoundException();
} else {
return defineClass(name, classData, 0, classData.length);
}
}
private byte[] loadClassData(String className) {
String fileName = root + File.separatorChar
+ className.replace('. ', File.separatorChar) + ".class";
try {
InputStream ins = new FileInputStream(fileName);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
int length = 0;
while((length = ins.read(buffer)) ! = -1) { baos.write(buffer, 0, length); }return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public String getRoot() {
return root;
}
public void setRoot(String root) {
this.root = root;
}
public static void main(String[] args) {
MyClassLoader classLoader = new MyClassLoader();
classLoader.setRoot("E:\\temp"); Class<? >testClass = null;
try {
testClass = classLoader.loadClass("com.neo.classloader.Test2");
Object object = testClass.newInstance(); System.out.println(object.getClass().getClassLoader()); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); }}}Copy the code
The core of the custom class loader is the retrieval of the bytecode file, which needs to be decrypted in the class if it is encrypted bytecode. Since this is just a demonstration, I have not encrypted the class file, so there is no decryption. Here are a few things to note:
1, pass the filename here need to be a class defining all name, namely com. Paddx. Test. The classloading. Test format, because defineClass method is according to the process of this format.
2. It is best not to overwrite the loadClass method, as this can easily break the parent delegate pattern.
3, this kind of Test class itself can be AppClassLoader class loading, so we can’t put the com/paddx/Test/classloading/Test. The class under the class path. Otherwise, the parent delegate mechanism will cause the class to be loaded by the AppClassLoader rather than by our custom class loader.
Please stamp: making the original https://github.com/wangzhiwubigdata/God-Of-BigData pay close attention to the public, push, interview, download resources, focus on more big data technology ~ data into the way of god ~ update is expected to 500 + articles, has been updated 50 + ~Copy the code