This is the 29th day of my participation in the August More Text Challenge
Java execution procedure
Let’s review the steps required to execute a Java program
- Writing Java code
- Compile the source code to class via javAC
- Load class into the JVM
Steps 1 and 2 involve the developer, while step 3 is the behavior of the JVM, transparent to the developer
The JVM class loading
Take a closer look at the first point, class loading the JVM
From the perspective of memory space, each space is allocated:
Refer to GC and JVM Parameters for details on each memory space.
From the perspective of class life cycle, it is divided into stages:
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.
1. The load
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
During the load phase, the virtual machine needs to do the following three things:
- Gets the binary byte stream that defines a class by its fully qualified name
- Transform the static storage structure represented by this byte stream into the runtime data structure of the method area.
- Generate a java.lang.Class object in memory that represents the Class and acts as an access point for the Class’s various data in the method area
How to load a. Class file
- Load directly from the local system
- Download the. Class file from the network
- Load. Class files from zip, JAR, etc archives
- Extract. Class files from a proprietary database
- Dynamically compile Java source files into.class files
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
Loading time
- When the application starts, all classes will not be loaded at once, because if they are loaded at once, memory resources are limited, which may affect the normal operation of the application. For example, A A =new A(),
The only time a class is actually loaded is when the object is created. When we test, the main thread class that has the main method is loaded first
- Java virtual machines have the preloading function. Class loaders don’t have to wait until a class is “first actively used” to load it. The JVM specification states that the JVM can anticipate loading a class, and if the class fails but the application doesn’t call it, the JVM doesn’t report an error. The JVM will only report a LinkAgeError if this class is called.
Loading way
Implicit load
- Creating class objects
- Use the static field of the class
- Creating a subclass object
- Static fields using subclasses
- At JVM startup, BootStrapLoader loads some classes that the JVM itself needs to run
- When the JVM starts, the ExtClassLoader loads special classes in the specified directory
- When the JVM starts, AppClassLoader loads the classes in the classpath path, as well as the class file for the main function
Explicitly load
- Classloader.loadclass (className) is not initialized
- Class.forName(String name, boolean initialize,ClassLoader loader); The class is loaded using the current class loader of the caller’s class, depending on whether initialize is initialized or not
2. The connection
2.1. Validation: To ensure the correctness of the loaded classes
Validation is the first step in the connection phase, which ensures that the byte stream in the Class file meets the requirements of the current virtual machine and does not compromise the security of the virtual machine itself. 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 ** -xVerify: None ** parameter to turn off most of the class validation to shorten the virtual machine class load time
2.2. Preparation: Allocate memory for static variables of the class and initialize them to default values
The preparation phase is the phase that formally allocates memory and sets initial values for class variables, all of which will be allocated in the method area. There are several points to note about this phase:
- 2.2.1 Only class variables (static) are allocated, not instance variables, which are allocated in the Java heap along with the object at instantiation.
- 2.2.2 The initial value set here is usually the default zero value of the data type (0, 0L, NULL, false, etc.) rather than the value explicitly assigned in Java code.
Public static int value = 3; The putStatic instruction that assigns value to 3 is stored in the class constructor method after the program is compiled, so assigning value to 3 will be executed during initialization
-
2.2.3 if a class field has a ConstantValue property in its field property list, that is, a ConstantValue that is both final and static, then in preparation the value variable is initialized to the value specified by the ConstValue property.
Public static final int value = 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
2.3. Resolution: The process by which the virtual machine replaces symbolic references in the constant pool with direct references (memory addresses)
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
Constant pool
- Literals: Close to the Java language level, such as strings, constants that declare final, etc
- Symbolic references: concepts that belong to aspects of compilation principles :1. Fully qualified names including classes and interfaces 2. Field names and descriptors 3. The name and descriptor of the method
Constant term structure
These contents, need to analyze the detailed structure of the class file, learn later
3. Initialize, assign the correct initial value to the static variable of the class
The last stage of class loading is completely dominated by the JVM, except for the loading stage, which we can participate in through a custom class loader. In the initialization phase, you actually start executing the program, which is the class converted from Java
The JVM is responsible for initializing classes, primarily 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 rules
Class initialization timing: Only active use of a class results in class initialization
Java programs can use classes in two ways:
- Active use: loads, joins, initializes static fields
- Passive use: only load, connect, not initialize the static field of the class
Active use of classes includes the following six types:
- Create an instance of the class, such as (1) new (2) reflection newInstance (3) serialization generate obj; Four bytecode instructions are encountered: new, getstatic, putstatic, and Invokestatic
- Access or assign a value to a static variable of a class or interface (note the difference between static and static final)
- Call a static method of a class
- Reflection (e.g. Class.forname (” Test “))
- If a subclass of a class is initialized, its parent class is initialized as well. Interface initialization does not cause the parent interface to be initialized (which is also static final). For static fields,
- Only classes that directly define this field are 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
- A class (Java Test) that is marked as the startup class when the Java VIRTUAL machine starts, and runs a main class directly using the java.exe command
Passive use, not active use of the six are passive
- 1. If a static field of a parent class is referenced by a subclass, the passive use of the subclass does not cause the subclass to be initialized
- 2. Use an array to define a class reference class. It is passive use of the class and does not trigger initialization of the class
- 2.1 Reason: In fact, the array is no longer of type E, and the ARRAY JVM of E will dynamically generate a new type during the runtime. The new type is: if it is a one-dimensional array, then: [I (int), [F (float), etc.) for base types (int/float, etc.
- 3. Constants are stored in the constant pool of the class in which the method is called at compile time. In essence, there is no direct reference to the class in which the constant is defined, so it does not trigger initialization of the class in which the constant is defined
/** * Created by Jack on 2018/9/28. */ public class classtest3 {public static void main(String[]) args) { String x = F.s; }} class F {// Since uuid.randomuuid ().tostring () is a run-time validation method, Static final String s = uuid.randomuuid ().toString(); Println ("Initialize class F"); static {// Here is system.out.println ("Initialize class F"); }}Copy the code
Clinit and init
When the class file is compiled, the compiler generates two methods added to the class file: clinit, the class initialization method, and init, the instance initialization method
Clinit:
Clinit refers to the class constructor, which is automatically merged by the JVM and invoked when the JVM first loads the class file, including static variable initialization statements and the execution of static blocks
It merges assignments to static variables
- Note that **(only declared, or final static)** will not trigger the assignment, after all, the previous preparation phase has already assigned a value of 0 by default
- A static{} block is generated, and the virtual machine ensures that the parent class executes before the child class
- If a class or interface does not have a static variable assignment and static{} statement block, it will not be generated by the JVM
- The order in which static variable assignments and static{} statement blocks are merged is determined by the order in which the statements appear in the source file.
init:
Called when the instance is created, the constructor includes:
- New operator
- Plain code block
- Call the Class or Java. Lang. Reflect. The Constructor object newInstance () method;
- Call the clone() method of any existing object;
- Through the Java. IO. ObjectInputStream class getObject () method deserialization.
Public class classtest2 {static {system.out.println ("cinit"); i = 3; //System.out.println(I); } private static int I = 1; { System.out.println("init"); } public static void main(String [] args) {new ClassInitTest2(); new ClassInitTest2(); String str = "str"; System.out.println(str); }} cinit init init STRCopy the code
Static vs. static final for initialization
Public class classFinalTest {public static int age = 20; Static {// If age is defined as static final, system.out.println (" statically initialized! ") is not executed. ); } public static void main(String args[]){ System.out.println(ClassInitFinalTest.age); }}Copy the code
Several cases in which class initialization will not be performed
- A reference to a static field of a parent class by a subclass triggers initialization of the parent class, not the subclass.
- Defining an array of objects does not trigger initialization of the class
- Class A referencing static final constants of class B does not cause class B to be initialized (see ClassInitFinalTest above)
- Fetching a Class object by its name does not trigger Class initialization. Such as
- System.out.println(Person.class);
- If initialize is false, Class initialization is not triggered when loading a Class using class.forname. This parameter tells the vm whether to initialize the Class.
- Initialization is also not triggered by the default loadClass method of a ClassLoader
End of life cycle
The Life cycle of a Java virtual machine ends in one of the following situations
- 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 loading test
I saw a piece of code that was interesting
Zhu on 2018/9/28. */ public static ClassInit {private static ClassInit singleton = new ClassInit(); public static int counter1; public static int counter2 = 0; private ClassInit() { counter1++; counter2++; } public static ClassInit getSingleton() { return singleton; }} /** * With output, Zhu on 2018/9/28. */ public class classTestMain {public static void main(String []args) { ClassInit classInitTest = ClassInit.getSingleton(); System.out.println("counter1="+classInitTest.counter1); System.out.println("counter2="+classInitTest.counter2); }}Copy the code
What is the output of this code?
counter1=1
counter2=0
Copy the code
- The entry must be classinitTestmain.main (), which is loaded from here and initialized
- Classinit.getsingleton (), which is used for the first time, so all static variables are set to their initial values from the loading phase to the preparation phase. At this time
public static int counter1 = 0;
public static int counter2 = 0;
private static ClassInit singleton = null;
Copy the code
- When ClassInit goes to initialization, it generates a class constructor, which merges the assignment of a static variable with a static statement block. Post-merge execution
public static int counter1 ; Public void clinit() {<clinit> ClassInit singleton = new ClassInit(); // (1) int counter2 = 0; // (2)}Copy the code
- The initialization phase executes the code in Clinit until (1), at which point both Counter1 and Counter2 become 1.
- Clinit code is executed during the initialization phase, and at (2), Counter2 is set to 0 again.
- After initialization, return to classinit.getSingleton () in the Main method; The main method continues until the output ends.
The above is the life cycle of a class. This article focuses on the loading part. As mentioned above, the loading phase has more control for developers than other phases. Let’s learn a little bit about class loaders
Class loader
- BootstrapClassLoader: Load path: System.getProperty(“java.class.path”) or specified directly through -xbootclasspath
Features: written in C language
Manual for loading paths: sun. Misc. The Launcher. GetBootstrapClassPath () getURLs ()
- ExtClassLoader: Load path: System.getProperty(“java.ext.dirs”) or directly specified by -djava.ext.dirs
Features: Inherits URLClassLoader
Manually obtain the loading path :(URLClassLoader) app.class.getclassloader ().getparent ()).geturls ()
- AppClassLoader: Load path: System.getProperty(“sun.boot.class.path”) or specified by -cp, -classpath
Features: Inherits URLClassLoader
Manually obtain the loading path: ((URLClassLoader) App. Class. GetClassLoader ()). The getURLs (). Through this getSystemClassLoader () can get AppClassLoader, ClassLoader(inherited from URLClassLoader). If parent is not specified, the default parent is AppClassLoader
The same class
How to determine a type instance in the JVM:
Same Class = same ClassName + PackageName + ClassLoader
In the JVM, types are defined in a data structure called SystemDictionary, which takes a classloader and the full class name as parameters and returns an instance of the type.
SystemDictionary looks like this:
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 class loader try to load the class, and only tries to load the class from its own classpath if the parent class loader cannot load the class
- The caching 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
Parental delegation model
The working process of parental delegation: If a classloader receives a classload request, it first does not load the class itself. Instead, it delegates the request to the parent classloader. This is true at every level of classloaders, so all load requests should eventually be sent to the top level of the starting classloader. Only when the parent class loader reports that it cannot complete the load request (the class is not in the scope it manages) will the child loader attempt to load it itself
-
When an AppClassLoader loads a class, it first does not attempt to load the class itself. Instead, it delegates the classloading request to the parent class loader, ExtClassLoader.
-
When ExtClassLoader loads a class, it doesn’t try to load the class itself in the first place. Instead, it delegates the class loading request to BootStrapClassLoader.
-
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 to load the class.
-
If the ExtClassLoader also fails to load, AppClassLoader is used to load it. If the AppClassLoader also fails to load, ClassNotFoundException is reported.
Javac -verbose checks that the running class is loaded with jar filesCopy the code
protected synchronized Class<? > loadClass(String name, Boolean resolve) throws ClassNotFoundException{ The findLoadedClass(String) method is actually the wrapped method of the findLoadedClass0 method, which checks the class name, while the findLoadedClass0 method is a native method that looks at objects in the JVM underneath. Class c = findLoadedClass(name); If (c == null) {// The class is not loaded try {if (parent! = null) {// If the class has not been loaded, we should first delegate the loading to the parent classLoader. c = parent.loadClass(name, false); } else {// Return a class loaded by bootstrap class loader, null if it does not exist 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. c = findClass(name); }} if (resolve) {resolveClass(c); } return c; }Copy the code
We can also see from the above method that when implementing our own loader, instead of overwriting the locaClass method, we override findClass() to ensure that the parent delegate model also implements our own methods
Why use the parent delegate model?
- Save system resources: Because this avoids reloading, there is no need for the child ClassLoader to load the class again when the parent has already loaded the class
- Ensure type safety of Java core libraries: If we don’t use this delegate pattern, we can use a custom String to dynamically replace the type defined in the Java core API at any time. This would be a huge security risk. Since strings are already loaded by the Bootstrcp ClassLoader at startup, a user-defined ClassLoader will never load a self-written String unless you change the default algorithm for ClassLoader searching classes in the JDK.
Custom loader
Why define your own class loader when the JVM already provides a default one?
If we want to load a class or jar from another location, for example: I want to load a class file on the network. After dynamic loading into memory, we need to call the method in this class to achieve business logic. In this case, the default ClassLoader does not meet our requirements, so we need to define our own ClassLoader
There are two steps to defining your own classloader:
- Inherited Java. Lang. This
- Override the findClass method of the parent class
The sample
Very simple two classes, method print out the respective class loaders
public class LoaderClass { public void loader(){ System.out.println("LoaderClass:"+this.getClass().getClassLoader()); LoaderClass1 class1 = new LoaderClass1(); class1.loader(); } } public class LoaderClass1 { public void loader() { System.out.println(this.getClass().getName() + " loader:"+this.getClass().getClassLoader()); }}Copy the code
Custom loader
- Override the findClass method to load it from the class file
- Build a class from bytes via defineClass
public class MyClassLoader extends ClassLoader { protected Class<? > findClass(String name) throws ClassNotFoundException { String root = "d:/"; byte[] bytes = null; String file = root + name.subString (name.lastIndexof (".")+1) + ".class"; InputStream ins = new FileInputStream(file); 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); } bytes = baos.toByteArray(); ins.close(); } catch (Exception e) { e.printStackTrace(); } return defineClass(name, bytes, 0, bytes.length); }}Copy the code
The test class
public class ClassLoaderTest { public static void main(String[]args) throws Exception { ClassLoaderTest test = new ClassLoaderTest(); System.out.println(test.getClass().getClassLoader()); Sun.misc.Launcher$AppClassLoader system.out.println (test.getClass().getClassLoader().getparent ())); Sun.misc. Launcher$ExtClassLoader system.out.println (test.getClass().getParent().getparent ())); Null / / / / output = = = = = test repeated load, the classpath LoaderClass. The class is = = = = = = = = = = = = = = = = = / / = = = = = = although specify this, But it is still output LoaderClass: sun. Misc. $AppClassLoader the Launcher / / = = remove the classpath LoaderClass. The class and can output LoaderClass: com. Jack. This. MyClassLoader / / and LoaderClass created in the object class loader is also MyClassLoader MyClassLoader classLoader = new MyClassLoader(); Class<? > loadClass = Class.forName("com.jack.classloader.LoaderClass", true, classLoader); Method startMethod = loadClass.getMethod("loader"); startMethod.invoke(loadClass.newInstance()); MyClassLoader classLoader1 = new MyClassLoader(); myclassLoader1 = new MyClassLoader(); Class<? > loadClass1 = Class.forName("com.jack.classloader.LoaderClass", true, classLoader1); System.out.println(loadClass.equals(loadClass1)); // print false}}Copy the code
The resources
Class load timing and the difference between the two display loads
JVM class loading mechanism – the process of class loading
The < init > and < clinit >
Analysis of class loading principle & Dynamic loading Jar/Dex
Active/passive use of Java classes