Summary: A Java Virtual machine provides a runtime environment for Java programs, and one of the important tasks is to manage the life cycle of classes and objects. The life cycle of the class. The life cycle of a class begins with the class being loaded, concatenated, and initialized to the end of the class being unloaded. When the class is in the life cycle, its two-level data is in the method area, and there is a corresponding class object in the heap that describes the class (when a Java program uses any one of the classes, the system creates a Java.lang.Class object for it). A Java program can use a class only if it is in the life cycle , such as invoking a static member of a class or creating an instance of a class.
One, the life cycle of Java virtual machines and programs
When a Java program is run with a Java command, a Java Virtual machine process is started. The process of a Java virtual machine process from boot to termination is called the life cycle of a Java virtual machine. In the following scenario, the Java Virtual machine ends the lifecycle.
01. The normal execution of the program ends;
02. The program in the execution due to abnormal or error and abnormal termination;
03. Executed the System.exit () or Runtime.getruntime (). exit ();
04. The Java Virtual machine process terminates due to an error in the operating system;
When a Java virtual machine is in the life cycle, its overall task is to run the Java program. The process by which a Java program starts running with termination is called the program's life cycle, and it is consistent with the life cycle of the Java Virtual machine.
When the Java program finishes running, the JVM process ends and the process's state in memory is lost. This is illustrated by a small example.
Public class a { publicstaticint A = 5;}
public class ATest1 { public static void main (string[] args) {a.a ++; System.out.println (A.A); // 6 }}
Public class ATest2 { publicstaticvoid main (string[] args) { System.out.println (A.A); // 5 }}
Observe the output, because ATest1 and ATest2 are running the JVM process two times, and its modifications to class A will be lost after the first run, and the second run of the JVM will initialize Class A again.
Some beginners will think that a in class A is a static member variable, and that the static variables of all instances in the same class share the same block of memory, wrongly thinking that the second run will output the result after the first change. But actually two runs of Java programs are in two different JVM processes, so data is not shared between two JVMs.
Ii. loading, connecting, and initializing classes
When a Java program needs to use a class, the Java virtual Opportunity ensures that the class has been loaded, connected, and initialized. The connection process includes validation, preparation, and resolution of these three sub-steps. These steps must be executed in exactly the following order.
01. Loading: Find and load binary data of class;
Loading specifically refers to reading the binary data in the class's. class file into memory, storing it in the method area of the run-time data area, and then creating a Java.lang.Class object in the heap to encapsulate the data structure of the class within the method area.
Java virtual machines can load binary data of classes from different sources through different classloader, including the following:
001. Loading class files from the local file system is also the most common way;
002. Extracting a class file from a jar, zip, or other type of archive (for example, a database-driven class used in JDBC programming is placed in a jar file, the JVM can load the required class file directly from the jar file);
003. Load the class file via the network;
004. Dynamically compile a Java source file into a class file and execute the load;
The end product that the class loads is the class object that is located in the heap area of the runtime data area. The class object encapsulates the data structures of the classes within the method area, and provides the Java program with an interface to access the class's structure pairs within the method area.
Classes are loaded by the class loader, which is typically provided by the JVM, which is also the basis for all programs to run, which are often referred to as the system ClassLoader by the JVM's supplied class loaders. In addition, developers can create custom class loaders by inheriting ClassLoader base classes.
The ClassLoader does not need to wait until a class is "first active" and then loads it, and the JVM specification allows the ClassLoader to preload it when it is expected that a class will be used. If the class file is missing or there is an error during the pre-loading process, the ClassLoader must wait until the class is first used (throwing a linkageerror error), and if the class has not been actively used by the program, then the ClassLoader will not error.
02. Connection: Includes validation, preparation and parsing of class binary data;
001. Validation: Ensure the correctness of the loaded class
When the class is loaded, it enters the validation phase. A connection is the merging of binary data from a class that has been read into memory into the JVM runtime environment. The first step of the connection is the validation of the class, which is intended to ensure that the class being loaded is made up of the correct internal structure and is consistent with other classes. If the JVM checks for errors, it throws the corresponding Error object.
Question: The binary data of Java classes generated by the Java compiler is sure to be correct, and why is the class being validated?
Because the JVM does not know exactly how a particular class file was created and where it came from, the class file could have been generated by a normal Java compiler, or it could have been maliciously created (destroying the JVM runtime environment through a malicious class file), and the validation of the class would improve the robustness of the program. Ensure that the program is executed safely.
The validation of a class mainly includes the following:
Structure checking of class files: Ensure class files conform to the fixed format of Java class files.
Semantic checking: Make sure that the class itself conforms to the syntax of the Java language (for example, a final type class has no subclasses, and the final type method is not overwritten).
Bytecode verification: Ensure that the byte stream can be executed securely by the JVM. Byte stream Represents a Java method (including static methods and instance methods), which consists of a sequence of cell-byte directives called opcode, followed by one or more operands after each opcode. The bytecode verification step checks that each opcode is legitimate, that is, whether it has a legitimate operand.
Binary-compatible checks: Ensure that classes that reference each other are coordinated. For example, the B method of Class B is called in the A method of Class A. When the JVM validates Class A, it checks to see if there is a B method of Class B in the method area and if it does not exist (this problem occurs when the version of Class A and Class B is incompatible), a nosuchmethoderror error is thrown.
002. Prepare: Allocate memory for static variables of the class and initialize them to default values
During the preparation phase, the JVM allocates memory for the static variables of the class and sets the default initial values. For example, the following conditions:
Public class Demo { publicstaticint a = 1; Public Static Long b; Static { = 2; }}
In the preparation phase, the static variable a of type int is allocated a memory space of 4 bytes and given a default value of 0, and a long type of static variable B allocates 8 bytes of memory space and gives the default value of 0.
003. Parsing: Converting a symbolic reference in a class to a direct reference
During the parsing phase, the JVM replaces the symbolic references in binary data with direct references. For example, in the a method of Class A, call B
The B method of the class.
Public class A { new B (); Public void A () { b.b (); This line of code is represented in the binary data of Class A as a symbol reference } }
In the Class A binary data, a symbolic reference to the B () method is included, consisting of the full name and the associated description of the B () method. During the parsing phase, the JVM replaces this symbolic reference with a pointer to the memory location of the Class B () method in the method area, which is a direct reference.
03. Initialize: Assigns the correct initial value to the static variable of the class;
During the initialization phase, the JVM executes class initialization statements and assigns the initial values to the static variables of the class. In the program, there are two ways to initialize a static variable: one is to initialize it at the declaration of a static variable, and the other is to initialize it in a static code block.
The following code, A and B are explicitly initialized, and C does not have an explicit initialization, it will be the newspaper default value of 0.
public class A { private Static int a = 1; // at the declaration of the variable public static long b; public static long C; static {b = 1;// }}
In this article, if not specifically stated, the static variables of a class refer to static variables that cannot be used as compile-time constants. Java compilers and virtual machines have special processing methods for compile-time constants, which can be used to refer to the initialization of classes in the following contexts.
Declaration statements for static variables, as well as static blocks of code, are treated as class initialization statements, and the JVM executes them sequentially in the order in which they are written in the class file.
Java Virtual machine initialization A class consists of the following steps:
1. If the class has not been loaded and connected, then load and connect first.
2. If there is a direct parent class in the class, and the parent class is not initialized, the immediate parent class is initialized first.
3. If there is an initialization statement in the class, execute it sequentially.
When initializing the immediate parent class of a class, you also need to repeat the above steps, which ensures that when the program actively uses a class, the class and all its parent classes, including both the direct and indirect parent classes, have been initialized. The first class initialized in the program is the object class.
When a class or interface is loaded, the Java Virtual Machine specification provides some flexibility for implementation, but it also strictly defines the timing of initialization, and all Java Virtual machine implementations must initialize each class or interface when it is "first active" by the Java program. Java programs can be divided into two types of classes: active use and passive use, which are described in detail in the initialization time of the following classes.
Iii. initialization timing of classes
The JVM initializes the program only if it actively uses a class or interface for the first time. Only the following 6 methods are considered active use of the class or interface by the program.
01. Create an instance of the class. Include the new keyword to create, or create an instance by reflection, cloning, and deserialization.
02. Call the static method of the class.
03. Access a static variable for a class or interface, or assign a value to the static variable.
04. Use the reflection mechanism to create a Java.lang.Class object that corresponds to a class or interface. For example Class.forName ("test") operation, if the system has not initialized the test class, this wave operation will cause the test class to be initialized and return the Java.lang.Class object corresponding to the test class.
05. Initializes a subclass of a class that all the parent classes of the child class will be initialized.
The class that is marked as the startup class at 06.JVM startup (run a main class directly using the Java.exe command). For example, for the Java Test command, the Test class is the startup class (the main Class), and the JVM initializes the main class first.
In addition to the above 6 cases, other methods are considered passive and do not cause initialization of the class. Here's an example to verify:
Public classA { Public Static Final intA = 2*3;//A is a compile-time constant Public Static FinalString str = "haha";//STR is a compile-time constant Public Static Final intB = (int) (Math.random ());//B is not a compile -time constant Static{System.out.println ("Init A"); } }
"Macro variable":
1. For a final type of static variable, if the value of the variable can be computed at compile time, then this variable is considered a compile constant. The use of compile-time constants for classes in Java programs is considered passive use of classes and does not result in the initialization of classes.
The above example because the compile time can calculate a is 6, so when the program accesses A.A, is the passive use of Class A, does not cause class A initialization.
Public class Test { publicstaticvoid main (string[] args) { System.out.println ( A.A);} }
Run the program in the test class: the console prints only 6 and does not print init A in the static code block.
When the Java compiler generates class-A classes, he does not save a symbolic reference to "A.A" in the byte stream of the main () method, but instead embeds the constant value 6 directly in the byte stream. Therefore, when the program accesses A.A, it is objectively necessary to initialize Class A. (When the JVM loads and connects to Class A, it does not allocate memory for its compile-time-constant A in the method area.) )
2. For a static variable of the final type, if the value of the variable cannot be computed at compile time, then the use of this variable by the program to the class is considered an active use of the class, leading to the initialization of the class
Public class Test { publicstaticvoid main (string[] args) { System.out.println ( A.B);} }
Access to the class A is not a compile-time B, and the console prints init a 4.
This wave operation JVM Initializes class A, allowing variable B to have specific memory and initial values within the method area.
3. When the JVM initializes a class, all its parent classes are required to be initialized, but this rule does not apply to the interface.
01. When initializing a class, the interface it implements is not initialized first.
02. When an interface is initialized, its parent interface is not initialized first.
Therefore, a parent interface is not initialized because its subinterface or implementation class is initialized, and only when the program first uses a static variable for a particular interface, the initialization of that interface is caused.
4. Only when a static variable or static method accessed by a program is actually defined in the current class or interface, can it be considered an active use of the class or interface. Observe the following example:
classfather{//Parent Class Static intA = 1; Static{System.out.println ("Init father"); } Static voidmethod () {System.out.println ("Father Method"); }}classSonextendsFather {Static{System.out.println ("Init son"); }} Public classDemo { Public Static voidMain (string[] args) {System.out.println (SON.A);//initializes only the parent class FatherSon.method (); }}
Console results:
init father
1
Father method
5. Call the LoadClass () method of the ClassLoader class to load a class that simply loads the class, not the active use of the class, and does not cause the initialization of the class. Using the Class.forName () static method causes the class to be forced to initialize.
Packagecn.lifecycle;classA {Static{System.out.println ("Init A"); }} Public classB { Public Static voidMain (string[] args)throwsException {ClassLoader loader= Classloader.getsystemclassloader ();//get the System class loaderClass objclass = Loader.loadclass ("Cn.lifecycle.a");//Load aSystem.out.println ("After load A"); System.out.println ("Before Init A"); Objclass= Class.forName ("Cn.lifecycle.a");//Initialize a }}
Console results:
After load A
Before Init A
Init A
Not finished ...
Analysis of the life cycle of the java-class