Class life cycle
1. Load: At this stage the JVM does three main things:
- Gets the binary byte stream defined by the class’s fully qualified name
- Transform the static storage structure represented by this byte stream into a method area runtime data structure
- A class object representing this class is generated in the heap to act as the data access point in the method area
Compared to the other stages of class loading, the loading stage is the most controllable stage, because we can use the system’s class loader to load, or we can use custom class loaders to load.
2. Validation: The main function is to ensure the correctness of the loaded class, which is the first step in the connection stage. That is, to determine that a loaded class file is not harmful to the JVM, it does four main things
- File format verification: Verifies that the. Class file byte stream complies with the class file format specification and can be processed by the current version of the VM. The main inside of this magic number, the main version number, constant pool and so on check.
- Metadata verification: it mainly conducts semantic analysis on the information described by bytecode to ensure that the information described conforms to the requirements of Java language specifications, such as verifying whether the class has a parent class, and whether the field methods in the class conflict with the parent class, etc.
- Bytecode validation: This is the most complex phase of the entire validation process, mainly through data flow and control flow analysis to determine that the program semantics are legitimate and logical. After verifying the data type in the metadata verification stage, this stage mainly analyzes the method of the class to ensure that the method of the class will not make weihai VIRTUAL machine security when running.
- Symbolic reference validation: This is the final stage of validation and occurs when the virtual machine converts symbolic references to direct references. It mainly validates information outside the class itself. The goal is to ensure that the parsing action completes.
The validation phase is an important but unnecessary phase for the entire class loading mechanism. If our code ensures that there are no problems, then there is no need to validate it because it takes time. Of course we can use -xverfity: None to turn off most validation.
3. Preparation: Allocates memory and sets initial values for class variables. This memory is allocated in the method area. At this stage, we need to pay attention to two types of variables and initial values:
- Class variables (static) allocate memory, but instance variables do not. Instance variables are allocated to the Java heap along with the instantiation of the object.
- The initial value here refers to the data type default value, not the value assigned by the display in the code
4. Parsing: Essentially the process by which the JVM converts symbolic references from a constant pool into direct references.
- Symbolic reference: With a set of symbols to describe the referenced targets, can be any form of literal, as long as it is could be unambiguous positioning to the target, like in a work, can use cherry (your English name) on your behalf, working status can be used to represent you, but no matter any way these are just a symbol (symbol), the code point to your references (symbols)
- Direct reference: A direct reference is a pointer to a target, a relative offset, or a handle that can be directly or indirectly located to the target. Depending on the memory implemented by the virtual machine, direct references are generally different from one virtual machine to another.
The parse action is mainly for class or interface, field, class method, interface method, method type, method handle, and call point qualifier 7 class symbol references.
5. Initialization: This is when the Java program code actually executes. We know that the class variable has been assigned once in the preparation phase. At the initialization stage, programmers can assign values according to their own needs. A one-sentence description of this stage is the process of executing the class constructor < clinit >() method.
During the initialization phase, we assign the correct initial values to the static variables of the class. The JVM is responsible for initializing the class, mainly class variables. There are two ways to initialize a class variable in Java:
- Declare class variables to specify initial values
- Use static code blocks to specify initial values for class variables
JVM initialization steps
- If the class has not already been loaded and connected, the program loads and connects the class first
- If the class’s immediate parent has not already been initialized, its immediate parent is initialized first
- If there are initializers in a class, the system executes those initializers in turn
Class initialization timing
- 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.pch.test “))
- If you initialize a subclass of a class, its parent class is initialized as well
- A class that is marked as a startup class when the Java virtual machine starts, using the java.exe command directly to run a main class
Notes:
- For static fields, only the class that directly defines the field is initialized, so referring to a static field defined in a parent class by its subclass triggers initialization of the parent class but not the subclass.
For example:
public class SuperClass { static { System.out.println("SuperClass init...." ); } public static int value = 1; } public class SubClass extends SuperClass{ static { System.out.println("SubClass init...." ); } public static final int CONST = 2; } public class Test { public static void main(String[] args) { System.out.println(SubClass.value); Output: SuperClass init.... 1Copy the code
- Referring to a class through an array definition does not trigger initialization of the class
SubClass[] subArrayClasses = new SubClass[2]; There is no outputCopy the code
- Calling a constant field of a class does not trigger initialization of the class.
public class Test1 { public static void main(String[] args) { System.out.println(SubClass.CONST); }} Output: 2Copy the code
6. Use
7. Remove
In the life cycle of a class, there is a fixed sequence of five phases: load, validate, prepare, initialize, and unload. The parse phase is not always the same. It sometimes starts after initialization to support Java runtime binding. The whole process of class loading includes five stages: loading, verification, preparation, parsing and initialization.
Class loader
Java comes with three classes of classloaders
- Bootstrap ClassLoader: The top-level loader that loads the core class libraries.
- Extention ClassLoader: extended ClassLoader
- Appclass Loader: also called SystemAppClass. Load all classes of the current application’s classpath.
Bootstrap ClassLoader > Extention ClassLoader > Appclass Loader
Hierarchical diagram
There are three ways to load classes
- The main class with the main() method is initialized by the JVM when the application is launched from the command line.
- Dynamically loaded with the class.forname () method, the initialization block (static{}) is executed by default.
- It is loaded dynamically through the classLoader.loadClass () method without performing an initialization block.
See why this is so, we know by looking at the source code: the Class. The class.forname (className) method, the method of internal actual call is Class class.forname (className, true, this); The second Boolean argument indicates whether the Class needs to be initialized. Class.forname (className) does by default. Once initialized, static block code execution of the target object is triggered. Classloader. loadClass(className); classloader. loadClass(className,false); classloader.loadClass (className,false); Then static blocks and static objects will not be executed.
Principle of parental delegation
Its workflow is as follows: when a class loader receives a class loading task, it will first hand it to its parent class loader to complete it, so the final loading task will be passed to the top level of the startup class loader, and only when the parent class loader cannot complete the loading task, it will try to execute the loading task.
Take a look at the loadClass implementation in classLoader
protected Class<? > loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class<? > c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent ! = null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats PerfCounter.getParentDelegationTime().addTime(t1 - t0); PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; }}Copy the code
Now it’s clear how parental delegation works
The advantage of using parental delegation is that if you load java.lang.Object, whichever loader loads the class will ultimately delegate to the top bootloader, ensuring that different classloaders will end up with the same Object. Parents delegate principle summary is: can avoid the repeated load, the parent class has been loaded, subclasses don’t need to load more secure, again good solved all the unity of the base class for the class loader problem, if you don’t use this way, the user can define the class loader to load the core API, will bring related concerns.
Custom class loaders
There are two main ways
- Follow the parent delegate model: Inherit the ClassLoader and override the findClass() method.
- Break the parent delegate model: Inherit ClassLoader and override the loadClass() method.
The first approach is generally recommended for customizing class loaders that adhere to the parent delegate model to the greatest extent possible.
Let’s look at the implementation steps
- Create a class that inherits the ClassLoader abstract class
- Override the findClass() method
- Call defineClass() in the findClass() method
Code implementation:
Public class TestFile {public void loadFile() {system.out.println (" loadFile "); public void loadFile() {system.out.println (" loadFile "); } } public class UserClassLoader extends ClassLoader { private String classPath; public UserClassLoader (String classPath) { this.classPath = classPath; } private byte[] loadByte(String name) throws Exception { name = name.replaceAll("\\.", "/"); FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class"); int len = fis.available(); byte[] data = new byte[len]; fis.read(data); fis.close(); return data; } protected Class<? > findClass(String name) throws ClassNotFoundException { try { byte[] data = loadByte(name); return defineClass(name, data, 0, data.length); } catch (Exception e) { e.printStackTrace(); throw new ClassNotFoundException(); }}}; } public class TestClassLoad { public static void main(String args[]) throws Exception { //TestFile UserClassLoader classLoader = new UserClassLoader ("D:/Test"); UserClassLoader ("D:/Test"); Class clazz = classLoader.loadClass("com.pch.test.TestFile"); Object obj = clazz.newInstance(); Method method = clazz.getDeclaredMethod("loadFile", null); method.invoke(obj, null); }}Copy the code
Break the parent delegate model
As mentioned above, the parent delegate model is not a mandatory constraint and can be broken by customizing the class loader implementation. So far, there have been three large-scale “broken” cases in the parental delegation model.
The first “break” of the parent delegation model actually happened before the parent delegation model appeared – before JDK1.2 was released. Because the parent delegate model was introduced after JDK1.2, and the class loaders and abstract java.lang.ClassLoader were introduced in JDK1.0, Java designers had to make some compromises when introducing the parent delegate model in the face of the existing implementation code for user-defined class loaders. For forward compatibility, a new proceted method, findClass(), was added in java.lang.classLoader after JDK1.2. Prior to this, users could inherit java.lang.classLoader only to override the loadClass() method. This is because the virtual class calls the loader’s private method loadClassInternal() during class loading, and the only logic for this method is to call its own loadClass(). After JDK1.2, users are no longer encouraged to override the loadClass() method. Instead, they should write their own class loading logic into the findClass() method. In the loadClass() logic, if the parent class loader fails to load, the user will call its own findClass() method to complete the load. This ensures that newly written classloaders conform to the parental delegation model.
Parents delegate second “destruction” of the model is the defects of the model itself and, as a result of parents delegation model well solves the base class reunification of each class loader (the base class loading by the top loader) and base class is called the “foundation”, because they always are called as code calls the API. But what if the base class also calls the user’s code? A good example of this is JNDI services. JNDI is now standard in Java. Its code is loaded by the startup class loader (put into rt.jar in JDK1.3), but JNDI is all about centralized resource management and lookup. What if it calls the code of the JNDI Interface Provider (SPI) deployed in the application’s classpath by an independent vendor implementation, but the bootloader cannot “know” some of the code? To solve this dilemma, the Java design team introduced a less elegant design: the Thread Context ClassLoader. This classloader can be set through the setContextClassLoader() method of the java.lang.Thread class. If it has not been set when the Thread is created, it will inherit one from the parent Thread; The class loader is the application class loader by default if it is not set at the global scope of the application. A thread context class loader, JNDI service using this thread context class loader for the need of SPI code, which is the parent class loader requested subclasses loader to finish class loading action, this kind of behavior is actually created parents delegation model hierarchy to reverse the use of class loaders, has violated the parents delegation model, But there is nothing you can do about it. All load actions in Java that involve SPIs, such as JNDI,JDBC,JCE,JAXB, and JBI, take this approach.
The third “break” of the parental delegation model is due to users’ desire for dynamic applications, such as the advent of OSGi. In the OSGi environment, class loaders are no longer a tree structure in the parent delegate model, but a network structure.
The last
Thank you for reading here, after reading what do not understand, you can ask me in the comments section, if you think the article is helpful to you, remember to give me a thumbs up, every day we will share Java related technical articles or industry information, welcome to pay attention to and forward the article!