1.JVM overview
definition:
The full name of JVM is the running environment of java Virtual Machine java program (the running environment of java binary bytecode)
Benefits:
- Write once, run everywhere (cross platform)
- Automatic memory management, garbage collection function
- Array subscript out of bounds check
- polymorphic
Compare the relations and differences among JVM, JRE and JDK. We can use a diagram to explain
The JVM architecture is shown in the figure
After a class is compiled from java source code (.java file) into java binary bytecode, it must pass through the class loader before it can be loaded into the JVM to run.
We usually put classes in the method area. The objects created by the class in the future are placed in the heap, and the objects in the heap will use the virtual machine stack, program counters and local development when calling methods.
When a method is executed, each line of code is executed line by line by the interpreter in the execution engine. The hot code in the method, that is, the frequently called code, is compiled and executed by the immediate compiler. GC will recycle the garbage.
We can call the functions provided by the operating system through the local method interface.
The memory structure of the JVM includes:
1. method area
2. program counter
3. virtual machine stack
4. local method stack
5. stack
2. program counter
2.1. definition
Program Counter Register
effect:
remember the execution address of the next jvm instruction
characteristic
thread private
there will be no memory overflow (the only structure in the memory structure that will not overflow)
In 2.2, we will explain the function and characteristics of the program counter.
2.2. Explanation of functions and characteristics
Binary bytecode JVM instructions Java source code 0: getstatic #20 // PrintStream out = System.out; 3: astore_1 // - 4: aload_1 // out.println(1); 5: iconst_1 // - 6: invokevirtual #26 // - 9: aload_1 // out.println(2); 10: iconst_2 // - 11: invokevirtual #26 // - 14: aload_1 // out.println(3); 15: iconst_3 // - 16: invokevirtual #26 // - 19: aload_1 // out.println(4); 20: iconst_4 // - 21: invokevirtual #26 // - 24: aload_1 // out.println(5); 25: iconst_5 // - 26: invokevirtual #26 // - 29: return
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
We can see the code. The first line is system Out is assigned to a variable, and the println() method is called in 4:. Then print 1,2,3,4,5 in sequence. These instructions cannot be directly handed over to the CPU for execution, but must be interpreted. It is responsible for interpreting byte code instructions one by one into machine code, and then the machine code can be handed over to the CPU for execution.
that is
Binary bytecode - > interpreter - > machine code ->cpu
In fact, the function of the program counter is to remember the execution address of the next JVM instruction during the execution of the instruction.
The numbers 0,3,4 in front of our binary bytecode above... We can understand them as addresses. Based on the address information, we can find the command to execute.
After each instruction is given to the CPU for execution, the program counter will put the address of the next instruction into the program counter. After an instruction is executed, the interpreter will get the address of the next instruction from the program counter. Then it is interpreted into machine code by the interpreter and handed to the CPU for execution. Then repeat the process.
Physically, the program counter is implemented through registers. Registers are the fastest read memory units in the CPU assembly.
Program counters are thread private
If the above codes are all running in thread 1, and thread 2 and thread 3 are running at the same time, when multiple threads are running, the CPU will allocate time slices to each thread and thread 1. If thread 1 does not finish running at the specified time, it will temporarily save the state and switch to thread 2, and thread 2 will execute its own code. After thread 2 finishes executing, continue to execute the code of thread 1. During thread switching, we should remember the execution address of the next instruction. Program counters are required. If thread 1 just starts to execute the 9th line of code, just at this time, the time slice runs out, and the CPU switches to thread 2 for execution, then it will record the address 10 of the next instruction in the program counter, and the program counter is thread private, which belongs to thread 1. When thread 2 finishes executing the code, thread 1 grabs the time slice, it will take the next line of code from its own program counter. Each thread has its own program counter
3. virtual machine stack
3.1. Stack features
The stack is similar to the bullet clip in real life. The most important feature of stack is LIFO.
As shown in the figure, 1 is the most advanced in the stack, and 3 is the last in the stack. However, when it comes out of the stack, 3 comes out first, and 1 comes out last. That is, they stack in the order of 1,2,3 and stack out in the order of 3,2,1
Virtual machine stack is the memory space we need when running threads. A thread needs a stack when running. If there are multiple threads in the future, it will have multiple virtual machine stacks.
Each stack can be regarded as composed of multiple stack frames. For example, each element 1,2,3 in the above figure can be regarded as a stack frame.
A stack frame corresponds to a method call in Java, that is, the stack frame is the memory required for each method to run. The memory required for the running of each method generally includes parameters, local variables, and return addresses, which all need to occupy memory. Therefore, when each method is executed, these memory should be allocated in advance.
When we call the first method stack frame, it will allocate stack frame space for the first method and push it into the stack. When the method is completed, it will take the method stack frame out of the stack and release the memory occupied by the method.
There may be multiple stack frames in a stack.
summary
Java virtual machine stacks
- The memory required for each thread to run is called virtual machine stack
- Each stack is composed of multiple stack frames, corresponding to the memory occupied by each method call
- Each thread can only have one active stack frame, corresponding to the currently executing method (at the top of the stack)
The active stack frame represents the method that the thread is executing.
3.2. Demonstration of stack
public class teststacks { public static void main(String[] args) throws InterruptedException{ method1(); } public static void method1(){ method2(1,2); } public static int method2(int a,int b){ int c=a+b; return c; } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
You can debug the above code to observe the changes in the stack.
Stack order: main->method1->method2
Stack order: method2->method1->main
3.3. Analysis of stack
- Does garbage collection involve stack memory?
No, garbage collection is just to collect useless objects in heap memory. Stack memory does not need to perform garbage collection on it. Stack memory is released as method calls end. - Is the larger the stack memory allocation, the better?
First, the stack memory can be specified: -xss size (if the stack memory size is not specified, different systems will have different default values)
Secondly, because the computer memory is certain, if there is 100Mb, if the stack memory is specified as 2Mb, there can only be 50 threads at most. Therefore, the larger the stack memory is, the better. Generally, the larger the stack memory is, the more method recursive calls can be made, which will not enhance the thread efficiency, but will reduce the number of threads. Generally, the default size is used.
3.4. Thread safety of stack
To see whether a variable is thread safe, the first thing is to see whether the variable is shared or private to multiple threads. Thread safety should be considered for shared variables.
Secondly, local variables cannot be guaranteed to be thread safe. It is necessary to see whether this variable escapes from the scope of the method (thread safety should be considered when escaping from the scope of the method as parameters and return values)
For example:
Local variables in the following code are private and thread safe
//Will multiple threads execute this method at the same time cause x value confusion? //No, because x is a local variable in the method, which is thread private and does not interfere with each other static void m1(){ int x=0; for(int i=0;i<5000;i++){ x++; } System.out.println(x); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
However, if we change the variable type to static, it will be very different. x is a static variable. Thread 1 and thread 2 have the same x at the same time. The static variable is shared by multiple threads. If it is not protected, thread safety problems will occur.
static void m1(){ static int x=0; for(int i=0;i<5000;i++){ x++; } System.out.println(x); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
Let's look at a few more ways
public static void main(String[] args) { StringBuilder sb = new StringBuilder(); sb.append(4); sb.append(5); sb.append(6); new Thread(()->{ m2(sb); }).start(); } public static void m1() { StringBuilder sb = new StringBuilder(); sb.append(1); sb.append(2); sb.append(3); System.out.println(sb.toString()); } public static void m2(StringBuilder sb) { sb.append(1); sb.append(2); sb.append(3); System.out.println(sb.toString()); } public static StringBuilder m3() { StringBuilder sb = new StringBuilder(); sb.append(1); sb.append(2); sb.append(3); return sb; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
m1 is thread safe: sb in m1 is a local variable in the thread, which is thread private
m2 thread is unsafe: sb is a method parameter. It may be accessed by other threads. It is no longer private to threads. It is shared by multiple threads.
m3 is not thread safe: it is returned as a return result. It is possible that other threads may get this object and modify it concurrently.
3.5. Stack memory overflow (StackOverflowError)
Under what circumstances will stack memory overflow occur?
1. too many stack frames lead to stack memory overflow (generally, too many recursive calls and too many stack entries lead to overflow)
The most common scenario here is the recursive invocation of functions.
2. stack memory overflow caused by too large stack frame (not easy to occur)
Stack memory overflow code demonstration 1 (self-developed):
Test the following program, where the recursive function has no recursive boundary
public class Demo1_2 { private static int count; public static void main(String[] args) { try { method1(); } catch (Throwable e) { e.printStackTrace(); System.out.println(count); } } private static void method1() { count++; method1(); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
The operation results are as follows
...
An error StackOverflowError is reported here.
A total of 22846 recursive calls were made
Set the stack memory size in idea:
Set the stack memory a little smaller and find that 5000 recursive calls overflow.
Stack memory overflow code demonstration 2 (the third-party dependent library appears):
In this case, the JsonIgnore annotation can be used to solve the circular dependency. During data conversion, only the department class is associated with the Employee class. The Employee class is no longer associated with the department class. The @JsonIgnore annotation is added to the Department attribute (dept) of the Employee class. Specific use details can be Click here to view
3.6. Thread run diagnostics
3.6.1. Case 1: too much CPU (linux system as an example)
Troubleshooting steps:
1. use the top command in linux to check the cpu usage of the background process
Notice that we ran a Java program before that
Java code takes up 99.3% of the CPU The top command can only locate a process, not a thread.
2. check the CPU usage of threads: ps H -eo pid,tid,%cpu
If there are too many displays, you can use ps H -eo pid,tid,%cpu | grep process id to filter out some processes you don't want to see
Note: ps can view not only the process, but also the CPU usage of the thread. H displays all the information about the threads in the process- eo specifies the output of the content of interest. Here we want to see the pid,tid and CPU usage%cpu
When the number of threads is too large and the troubleshooting is inconvenient, we can use grep pid to filter out the processes that are not interested
ps H -eo pid,tid,%cpu |grep 32655
3. locate the thread that occupies too much memory, and then use the command (jstack+ process id) provided by Jdk to view the running information of each thread in the process. You need to convert the thread ID (decimal) found in step 2 to hexadecimal, and then compare and query the location to determine the exception information.
Thread1, thread2 and thread3 are threads defined by ourselves.
You can find the problematic thread according to the thread id and further locate the source code line number of the problem code
3.6.2. Case 2: thread diagnosis_ Delay in getting results
You can still view the running information of each thread in the process through the jstack+ process id provided by the jdk
4. local method stack
Meaning: when the Java virtual machine calls a local method, it needs to provide some memory space for the local method
Local methods are not code written by Java. Sometimes Java cannot directly interact with the operating system, so it needs to use C/C++ language to interact with the operating system. Then Java can obtain these functions by calling local methods. There are many local methods, such as clone() method, hashCode method, wait method and notify method of Object class
public native int hashCode();
- 1
5. stack
5.1. definition
1. virtual machine stack, program counter and local method stack are all private to threads, while heap and method area are a memory area shared by threads.
2. all objects created with the new keyword will use heap memory
3. since the heap is shared by threads, thread safety should be considered for all objects in the heap (with some exceptions)
4. the heap has a garbage collection mechanism, and objects that are no longer referenced will be recycled
5.2. Heap memory overflow (OutOfMemoryError:Java heap space)
Objects always exist in the heap and are not recycled, and occupy more and more memory, resulting in heap memory overflow (although there is a garbage collection mechanism in the heap, the garbage collection mechanism does not recycle all objects)
We can look at the following code
public static void main(String[] args) { int i = 0; try { List<String> list = new ArrayList<>(); String a = "hello"; while (true) { list.add(a); // hello, hellohello, hellohellohellohello ... a = a + a; // hellohellohellohello i++; } } catch (Throwable e) { e.printStackTrace(); System.out.println(i); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
Error java lang.OutOfMemoryError
A hello is spliced in the code every time. Since the defined list set is created in the try statement, the list set will not be recycled during the continuous execution of the for loop. As long as the program has not reached the catch, it will remain valid. String objects are appended to the collection. Since string objects are always used, they will not be recycled.
We can set the heap space size through -Xmx.
We changed the heap memory to 8m (the previous memory was 4G), and only ran it 17 times.
5.3. Heap memory diagnostics
1.jps tool: JPS, to view which Java processes are in the current process and display the process id (enter the command through the terminal command line in idea)
2.jmap tool: jmap -heap process id queries the heap memory usage at a certain time
3.jconsole tool: graphical interface, multi-functional monitoring tool, which can continuously monitor. The use flow chart is as follows (1-2-3):
6. method area
6.1. definition
Like the Java heap, the Method Area is a memory area shared by all threads. It is used to store class information, constants, static constants, code compiled by the instant compiler and other data that have been loaded by the virtual machine. (information about classes). Although the Java virtual machine specification describes the Method Area as a logical part of the heap, it has an alias called non heap, which should be distinguished from the Java heap. The Method Area is created when the virtual machine starts.
For developers who are used to developing and deploying programs on the HotSpot virtual machine, many prefer to call the method "Permanent Generation". In essence, the two are not equivalent, just because the design team of the HotSpot virtual machine chooses to extend the GC generational collection to the method area, or use the Permanent Generation to implement the method area, In this way, the garbage collector of HotSpot can manage this part of memory like managing Java heap, which can save the work of writing memory management code for the method area. For other virtual machines (such as BEA JRockit, IBM J9, etc.), there is no concept of Permanent Generation. In principle, how to implement the method area belongs to the implementation details of the virtual machine and is not subject to the virtual machine specification. However, it seems not a good idea to use the Permanent Generation to implement the method area, because it is easier to encounter the memory overflow problem (the Permanent Generation has the upper limit of -XX:MaxPermSize. As long as J9 and JRockit do not touch the upper limit of the process's available memory, such as 4GB in a 32-bit system, there will be no problem), Moreover, there are very few methods (such as String.intern()) that can lead to different performance in different virtual machines. Therefore, according to the officially released roadmap information, the HotSpot virtual machine has also abandoned the Permanent Generation and gradually adopted the navitive memory to realize the planning of the method area. In the HostSpot of JDK1.7, the string constant pool originally placed in the Permanent Generation has been removed, which is later referred to as the meta space in jdk1.8, and the operating system memory used.
The Java virtual machine specification has very loose restrictions on the method area. In addition to the fact that Java heap does not require continuous memory and can be noisy, fixed or extensible, you can also choose not to implement garbage collection. Relatively speaking, garbage collection rarely occurs in this area, but it is not as "permanent" as the name of the permanent generation when the data enters the method area. The memory recycling goal in this area is mainly aimed at the recycling of constant pools and the unloading of types. Generally speaking, the recycling "performance" of this area is not satisfactory, especially for the unloading of types. The conditions are quite harsh, but the recycling of this part of the area is indeed necessary. In the BUG list of Sun Corporation, several serious bugs have occurred because the lower version of the HotSpot virtual machine has not fully recycled this area, resulting in memory leakage.
According to the Java virtual machine specification, when the method area cannot meet the memory allocation requirements, an OutOfMemoryError exception will be thrown.
Definition of virtual machine in the original text:
6.2. definition
Before jdk1.8, the method area is the heap memory used. After 1.8, the method area is the operating system memory used.
This is not very clear. Please refer to this blog Click to view
Constant pools are divided into static constant pools and dynamic constant pools. Constant pools in the following figure refer to dynamic constant pools because they have been read into memory, while static constant pools exist in class files
6.3. Method area memory overflow (OutOfMemoryError: Metaspace)
Permanent generation memory overflow before 1.8
Meta space memory overflow will occur after 1.8
/** * Demo meta space memory overflow java lang.OutOfMemoryError: Metaspace * -XX:MaxMetaspaceSize=8m */ public class Demo1_8 extends ClassLoader { // Binary bytecode that can be used to load classes public static void main(String[] args) { int j = 0; try { Demo1_8 test = new Demo1_8();<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> <span class="token number">10000</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">,</span> j<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// ClassWriter is used to generate binary bytecode of a class</span> ClassWriter cw <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ClassWriter</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//Parameters: version number, public, class name, package name, parent class, interface</span> cw<span class="token punctuation">.</span><span class="token function">visit</span><span class="token punctuation">(</span>Opcodes<span class="token punctuation">.</span>V1_8<span class="token punctuation">,</span> Opcodes<span class="token punctuation">.</span>ACC_PUBLIC<span class="token punctuation">,</span> <span class="token string">"Class"</span> <span class="token operator">+</span> i<span class="token punctuation">,</span> null<span class="token punctuation">,</span> <span class="token string">"java/lang/Object"</span><span class="token punctuation">,</span> null<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Generate class, binary bytecode is represented by byte, and byte[]</span> <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> code <span class="token operator">=</span> cw<span class="token punctuation">.</span><span class="token function">toByteArray</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Class loading performed</span> test<span class="token punctuation">.</span><span class="token function">defineClass</span><span class="token punctuation">(</span><span class="token string">"Class"</span> <span class="token operator">+</span> i<span class="token punctuation">,</span> code<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> code<span class="token punctuation">.</span>length<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Class object</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{<!-- --></span> System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>j<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
After jdk1.8, by default, the method area uses system memory, so increasing it will not lead to memory overflow. The loop runs successfully many times.
When -XX:MaxMetaspaceSize=8m is set, it overflows after 5411 times. Java Lang.outofmemoryerror: Metaspace error
Before 1.8, the error of persistent generation Overflow message was java lang.OutOfMemoryError:PermGen space
6.4. Constant pool
The constant pool is a table. The virtual machine instruction finds the class name, method name, parameter type and literal information (such as string constant, true and false) to be executed according to this constant table.
Runtime constant pool, constant pool is Class file. When the class is loaded, its constant pool information will be put into the runtime constant pool, and the symbolic address inside will be changed into the real address *.
public class HelloWorld { public static void main(String[] args) { System.out.println("hello,world"); } }
- 1
- 2
- 3
- 4
- 5
The above is a helloworld program. To run helloworld, you must first compile it into a binary bytecode.
Binary bytecode is defined by basic class information, constant pool and class method (including virtual machine instructions).
Decompile HelloWorld (you need to run to compile.java files into.class files)
Using the idea tool
F:\IDEA\projects\jvm>javap -v F:\IDEA\projects\jvm\out\production\untitled\HelloWorld.class
- 1
F:\IDEA\projects\jvm\out\production\untitled\ is helloworld Path of class
Show class details
Classfile /F:/IDEA/projects/jvm/out/production/untitled/HelloWorld.class Last modified 2021-1-30; size 533 bytes MD5 checksum 82d075eb7217b4d23706f6cfbd44f8f1 Compiled from "HelloWorld.java" public class HelloWorld minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
You can see the class file, the last modification time, and the signature. And version, etc. Some also have details such as access modifiers, parent classes, and interfaces.
Show constant pool
Constant pool: #1 = Methodref #6.#20 // java/lang/Object."<init>":()V #2 = Fieldref #21.#22 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #23 // hello,world #4 = Methodref #24.#25 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = Class #26 // HelloWorld #6 = Class #27 // java/lang/Object #7 = Utf8 <init> #8 = Utf8 ()V #9 = Utf8 Code #10 = Utf8 LineNumberTable #11 = Utf8 LocalVariableTable #12 = Utf8 this #13 = Utf8 LHelloWorld; #14 = Utf8 main #15 = Utf8 ([Ljava/lang/String;)V #16 = Utf8 args #17 = Utf8 [Ljava/lang/String; #18 = Utf8 SourceFile #19 = Utf8 HelloWorld.java #20 = NameAndType #7:#8 // "<init>":()V #21 = Class #28 // java/lang/System #22 = NameAndType #29:#30 // out:Ljava/io/PrintStream; #23 = Utf8 hello,world #24 = Class #31 // java/io/PrintStream #25 = NameAndType #32:#33 // println:(Ljava/lang/String;)V #26 = Utf8 HelloWorld #27 = Utf8 java/lang/Object #28 = Utf8 java/lang/System #29 = Utf8 out #30 = Utf8 Ljava/io/PrintStream; #31 = Utf8 java/io/PrintStream #32 = Utf8 println #33 = Utf8 (Ljava/lang/String;)V
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
Display method definition
{ public HelloWorld(); 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 1: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this LHelloWorld;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String hello,world
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 3: 0
line 4: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 args [Ljava/lang/String;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
The first method is public HelloWorld(); It is a parameterless constructor that the compiler automatically constructs for us.
The second is public static void main(java.lang.String[]); The main method
Fanga contains the instructions of the virtual machine.
getstatic gets a static variable, that is, system Out static variable
ldc is to load a parameter. The parameter is the string hello,world
invokevirtual virtual method call, println method
return execution ends.
We have \2, \3, \4 after getstatic, ldc and invokevirtual. When the interpreter translates these virtual machine instructions, it will perform a table lookup translation of these \2, \3, \4. For example, getstatic \2, check the constant pool table. In constant pool
#2 = Fieldref #21.#22 refers to the member variable \21, \22
#21 = Class #28 // java/lang/System
#22 = NameAndType #29:#30 // out:Ljava/io/PrintStream;
Then go to \28.29, 30
#28 = Utf8 java/lang/System
#29 = Utf8 out
#30 = Utf8 Ljava/io/PrintStream;
So now I know. I want to find java A member variable called out under the lang.system class. Its type is java/io.
Similarly, ldc is to find \3 = string \23 utf8 Hello, world, which is a string of the virtual machine constant pool. Load the helloworld constant into a string object.
invokevirtual #4 Methodref #24.#25 wait
So the function of constant pool is to provide some constant symbols for our instructions. According to these constant symbols, we can find them by looking up the table, so that the virtual machine can successfully execute them.