JVM class loading and class loader
class loading
The life cycle of a class is composed of 7 stages, as shown in the figure
First, the loading stage of the
class 1. Obtain the class file that stores the class through the full naming of the class ( acquisition method )
2. Parse into runtime data, that is, instanceKlass instance, stored in the method area
3. Generate the class object of this class in the heap area, namely the instanceMirrorKlass instance
When to load
-When actively using
1、new、getstatic、putstatic、invokestatic
2. Reflection
3. Initializing a subclass of a class will load its parent class
4. Startup class (the class where the main function is located)
5. When using jdk1.7 dynamic language support, if the final analysis result of a java.lang.invoke.MethodHandle instance has REF_getstatic, REF_putstatic, and REF_invokeStatic method handles, and the class corresponding to this method handle has not been initialized, you need to first Trigger its initialization
-Pre-loading: wrapper class, String, Thread
Obtaining methods such as
1. Read from the compressed package, such as jar, war
2. Obtain from the Internet, such as Web Applet
3. Dynamic generation, such as dynamic proxy, CGLIB
4. Generated by other files, such as JSP
5. Read from the database
6. Read from encrypted files
Verification phase
1. File format verification
2. Metadata verification
3. Bytecode verification
4. Symbol reference verification
preparation stage
Allocate memory for static variables and assign initial values: for example, int = 0; long = 0L;
When the instance variable is assigned when the object is created, there is no saying that the initial value is assigned
If the variable is modified by final, then the constantValue attribute will be added to the attribute when compiling, and the assignment will be completed directly in the preparation phase (compiled)
public static int a = 20;
public static final int b = 30;
public static void main(String[] args) {
//不运行直接编译
}
Parsing stage
Popular point is that indirect reference -> direct reference
Indirect reference: a reference to the runtime constant pool
Direct reference: memory address
Still use this Test object, let's take a look at the constant pool information after compilation and running
public static int a = 20;
public static final int b = 30;
public static void main(String[] args) {
for (;;);//运行中
}
1. After compiling, switch to the classes directory and use javap -verbose or jclasslib
For example, mine is D:\spring-boot\boot\target\classes>javap -verbose com.waf.boot.wk.Test The result obtained is a reference to the constant pool
javap -verbose
D:\spring-boot\boot\target\classes>javap -verbose com.waf.boot.wk.Test
Classfile /D:/spring-boot/boot/target/classes/com/waf/boot/wk/Test.class
Last modified 2021-3-24; size 566 bytes
MD5 checksum d7655afea6f7cf4d8b5ab6005cd334ab
Compiled from "Test.java"
public class com.waf.boot.wk.Test
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#26 // java/lang/Object."<init>":()V
#2 = Fieldref #3.#27 // com/waf/boot/wk/Test.a:I
#3 = Class #28 // com/waf/boot/wk/Test
#4 = Class #29 // java/lang/Object
#5 = Utf8 a
#6 = Utf8 I
#7 = Utf8 b
#8 = Utf8 ConstantValue
#9 = Integer 30
#10 = Utf8 <init>
#11 = Utf8 ()V
#12 = Utf8 Code
#13 = Utf8 LineNumberTable
#14 = Utf8 LocalVariableTable
#15 = Utf8 this
#16 = Utf8 Lcom/waf/boot/wk/Test;
#17 = Utf8 main
#18 = Utf8 ([Ljava/lang/String;)V
#19 = Utf8 args
#20 = Utf8 [Ljava/lang/String;
#21 = Utf8 StackMapTable
#22 = Utf8 MethodParameters
#23 = Utf8 <clinit>
#24 = Utf8 SourceFile
#25 = Utf8 Test.java
#26 = NameAndType #10:#11 // "<init>":()V
#27 = NameAndType #5:#6 // a:I
#28 = Utf8 com/waf/boot/wk/Test
#29 = Utf8 java/lang/Object
{
public static int a;
descriptor: I
flags: ACC_PUBLIC, ACC_STATIC
public static final int b;
descriptor: I
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
ConstantValue: int 30
public com.waf.boot.wk.Test();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/waf/boot/wk/Test;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=0, locals=1, args_size=1
0: goto 0
LineNumberTable:
line 8: 0
LocalVariableTable:
Start Length Slot Name Signature
0 3 0 args [Ljava/lang/String;
StackMapTable: number_of_entries = 1
frame_type = 0 /* same */
MethodParameters:
Name Flags
args
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: bipush 20
2: putstatic #2 // Field a:I
5: return
LineNumberTable:
line 5: 0
}
SourceFile: "Test.java"
Mainly look at Constant pool:
#3 = Class #28 // com/
waf /boot/wk/Test jclasslib gets a reference to the constant pool
2. After running, the real memory address can be seen in HSDB through the process number
Initialization phase
Execute the static code block to complete the assignment of static variables
Static fields, static code segments, and clinit methods are generated at the bytecode level
The order of the statements in the method is related to the order in which the code is written
We can look at a few examples of static
public class Test_1 {
public static void main(String[] args) {
System.out.println(Test_1_B.str);
}
}
class Test_1_A{
static {
System.out.println("a static");
}
}
class Test_1_B extends Test_1_A{
public static String str = "a str";
static {
System.out.println("b static");
}
}
//output
a static
b static
a str
public class Test_1 {
public static void main(String[] args) {
System.out.println(Test_1_B.str);
}
}
class Test_1_A{
public static String str = "a str";
static {
System.out.println("a static");
}
}
class Test_1_B extends Test_1_A{
static {
System.out.println("b static");
}
}
//output
a static
a str
How is this static field stored in klass-look at Test_1_A & Test_1_B in the second example through the HSDB tool
Stored in instanceMirrorKlass (Oop) and Test_1_B has no str attribute (only the inheritance relationship of Java syntax exists). How did the bottom layer find it (I don’t understand...)
public class Test_1 {
public static void main(String[] args) {
System.out.println(new Test_1_B().str);
}
}
class Test_1_A{
public String str = "a str";
static {
System.out.println("a static");
}
}
class Test_1_B extends Test_1_A{
static {
System.out.println("b static");
}
}
//output
a static
b static
a str
The way JVM reads static properties (example two) (how did you find it at the bottom (didn't understand...))
str is a static attribute of the class Test_1_A, you can see that it will not be stored in the mirror class of the subclass Test_1_B
As you can guess, there are two ways to access the static fields of the parent class Test_1_A through the subclass Test_1_B:
1. First go to the mirror class of Test_1_B to get it, if there is a direct return; if not, the request will be thrown up along the inheritance chain. Obviously, the performance of this algorithm increases with the death of the inheritance chain, and the algorithm complexity is O(n)
2. With the aid of another data structure, the KV format is used for storage, and the query performance is O(1)
Hotspot is the second method used. With the help of another data structure ConstantPoolCache, there is an attribute _cache in the constant pool class ConstantPool that points to this structure. Each piece of data corresponds to a class ConstantPoolCacheEntry.
Where is ConstantPoolCacheEntry? Behind the ConstantPoolCache object, look at the code C++
\openjdk\hotspot\src\share\vm\oops\cpCache.hpp
ConstantPoolCacheEntry* base() const { return (ConstantPoolCacheEntry*)((address)this + in_bytes(base_offset())); }
This formula means the address of the ConstantPoolCache object plus the memory size of the ConstantPoolCache object
ConstantPoolCache
The constant pool cache is a runtime data structure reserved for the constant pool. Interpreter runtime information that stores all field access and call bytecodes. The cache is created and initialized before the class is actively used. Each cache item is filled when it is parsed
How to read
\openjdk\hotspot\src\share\vm\interpreter\bytecodeInterpreter.cpp
CASE(_getstatic): { u2 index; ConstantPoolCacheEntry * cache; index = Bytes::get_native_u2(pc+1); // QQQ Need to make this as inlined as possible. Probably need to // split all the bytecode cases out so c++ compiler has a chance // for constant prop to fold everything possible away. cache = cp->entry_at(index); if (!cache->is_resolved((Bytecodes::Code)opcode)) { CALL_VM(InterpreterRuntime::resolve_get_put(THREAD, (Bytecodes::Code)opcode), handle_exception); cache = cp->entry_at(index); } ……
As can be seen from the code, it is directly to get ConstantPoolCacheEntry
Class loader