Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Example Analysis of Java Virtual Machine Stack

2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

Editor to share with you the example analysis of the Java virtual machine stack, I believe that most people do not know much about it, so share this article for your reference, I hope you can learn a lot after reading this article, let's go to know it!

Overview of Java virtual machine stack

The Java virtual machine stack (Java Virtual Machine Stacks) is thread private and has the same life cycle as threads. Virtual machine stack describes the memory model of Java method execution: stack Stack Frame is a data structure used to support method invocation and execution of Java virtual machine, and it is a stack element in virtual machine stack. Each method creates a stack frame to store local variables, Operand stacks, dynamic links, method exits, and so on.

When compiling program code, how many local variables are needed in the stack frame and how deep the Operand stack has been fully determined, and written into the Code attribute of the method table, so how much memory a stack frame needs to allocate will not be affected by the program runtime variable data, but only depends on the specific virtual machine implementation.

The process of each method from the call to the completion of execution corresponds to the process of a stack frame from entering the stack to leaving the stack in the virtual stack (to say human words is to execute a method to press the stack frame of the method into the top of the stack. Method execution completes its stack frame off the stack). In JVM, there are only two operations for stack frames: off-stack and on-stack. The method being executed by the thread is called the current thread method, and the stack frame of this method is called the current frame, and the execution engine runtime is only valid for the current stack frame.

Each component of the stack frame is described below.

Local variable scale

The Local variable scale (Local Variable Table) is a set of variable value storage spaces for storing method parameters and local variables defined within the method. When the Java program is compiled into a Class file, the maximum capacity of the local variable table that the method needs to allocate is determined in the max_locals data item of the method's code property.

The capacity of the local variable table is in the minimum unit of variable slot (Variable Slot, hereinafter referred to as Slot). The virtual machine specification does not specify the amount of memory space that an Slot should occupy, but it is very instructive to say that each reference should be able to store data of boolean, byte, char, short,int,float, reference or returnAddress types, all of which can be stored using 32-bit or less physical memory. But this description is somewhat different from explicitly stating that "each Slot occupies 32 bits of memory space," and the length at which it runs Slot can vary depending on the processor, operating system, or virtual machine. As long as you ensure that 64-bit physical memory space is used to implement a Slot in a 64-bit virtual machine, the virtual machine still uses alignment and padding to make the Slot look the same as in the 32-bit virtual machine.

A Slot can store a data type of less than 32 bits. The data types that occupy less than 32 bits in Java are boolean, byte, char, short, float, reference and returnAddress8. The seventh reference class represents a reference to an object instance, and the virtual machine specification does not specify its length or how the reference should be structured. But generally speaking, the virtual machine implementation should be able to do at least two things through this reference, one is to directly or indirectly find the starting address index of the data storage of the object in the Java heap from this reference, and the other is to directly or indirectly find the type information of the data type to which the object belongs in the method area. The eighth type, the returnAddress type, is now rare and has been replaced by exception tables.

For 64-bit data types, the virtual machine allocates two consecutive referenced Slot spaces in a high-bit alignment. As specified in the Java language (the reference type may be 32-bit or 64-bit), the only 64-bit data types are long and double. The virtual machine uses the local variable table by index positioning, and the index value ranges from 0 to the maximum number of Slot of the local variable table. If you are accessing a variable of 32-bit data type, index n represents the use of the nth Slot. If it is a variable of 64-bit data type, it is stated that two Slot will be used at the same time. For two adjacent Slot that store a 64-bit data together, one of them is not allowed to be accessed separately in any way. The Java virtual machine specification clearly requires that if a bytecode sequence for this operation is encountered. The virtual machine should throw an exception during the verification phase of class loading.

If it is an instance method (a non-static method), the Slot of the zero-bit index in the local variable table defaults to the reference "this" used to pass the object instance to which the method belongs. The rest of the parameters are arranged according to the order of the parameter table, occupying the local variable Slot starting from 1. After the parameter table is assigned, the remaining Slot is allocated according to the order and scope of the variables defined inside the method body (for example, method method (int a _ 1 and a2), and the parameter tables are A1 and a2. Then the local variable table indexes 0, 1, and 2 store this pointers, A1, a2, respectively, if there are other internal variables inside the method. Then there is a position after a2 in the local variable scale.

In order to save the stack frame space as much as possible, the Slot in the local variable table can be reused, and the scope of the variable defined in the method body does not necessarily cover the whole method body. If the value of the current bytecode PC counter has exceeded the scope of a variable, then the corresponding Slot of this variable can be handed over to other variables.

Local variables do not have a "preparation phase" like class member variables. We know that class variables have two processes of assigning initial values to the system, once in the preparation phase, and the other time in the initialization phase, assigning programmers defined initial values. Therefore, even if the programmer does not assign a value to the class variable during the initialization phase, the class variable still has a definite initial value. But local variables are different. If a local variable is defined but not assigned an initial value, you can't use it. Don't assume that there are default values in Java, such as integer variables default to 0, Boolean variables default to false, etc.

Operand stack

The Operand Operand Stack, also known as the Operand stack, is a last-in, first-out (Last In First out,LIFO) stack. Like the local variable table, the maximum depth of the Operand stack is written to the max_stacks data item of the code property at compile time. Each element of the Operand stack can be any Java data type, including long and double. The stack capacity of 32-bit data types is 1. The stack capacity of 64-bit data types is 2. At any time when the method is executed, the depth of the Operand stack does not exceed the maximum set in the maxstacks data item.

When a method is just executed, the Operand stack of the method is empty. During the execution of the method, there are various bytecode instructions to write and extract contents to the Operand stack, that is, out / on-stack operations. For example, when doing arithmetic operations, it is done through the Operand stack, or when other methods are called, parameters are passed through the Operand stack. For example, the bytecode instruction iadd for integer addition has stored two int values in the Operand stack closest to the top of the stack at run time. When this instruction is executed, the two int values will be removed from the stack and added, and then the added result will be added to the stack.

The interpretation execution engine of the Java virtual machine is called "stack-based execution engine", in which the "stack" is the Operand stack. If the stack depth requested by the current thread is greater than the maximum allowed by the virtual machine, a StackOverflowError exception is thrown.

Dynamic connection

Each stack frame contains a reference to the method to which the stack frame belongs in the runtime constant pool, which is held to support dynamic connections (Dynamic Linking) during method calls. There are a large number of symbolic references in the constant pool of the Class file, and the method call instructions in the bytecode take the symbolic references that point to the method in the constant pool as parameters. Some of these symbolic references are converted to direct references during the class loading phase or the first time they are used, which is called static parsing. The other part is converted to a direct reference during each run, which is called a dynamic connection.

When Java code compiles Javac, it does not have the step of "connect" like C and C++, but dynamically connects when the virtual machine loads the Class file. In other words, the final memory layout information of each method and field will not be saved in the Class file, so the symbolic references of these fields and methods cannot get the real memory entry address without runtime conversion, and can not be directly used by the virtual machine. When the virtual machine is running, it needs to obtain the corresponding symbol reference from the constant pool, and then parse and translate to the specific memory address when the class is created or run.

Math math=new Math (); math.compute (); / / call instance method compute ()

Take the above two lines of code as an example to explain the dynamic connection: when math.compute () is called, compute () calls the symbol. You need to use the symbol compute () to go to the constant pool to find the symbol reference of the corresponding method. The runtime will find the memory address of the bytecode instruction of the method through the symbol reference.

Return address of the method

When a method starts to execute, there are only two ways to exit the method. The first way is to execute the bytecode instruction returned by any method. At this time, the return value may be passed to the upper method caller (the method that calls the current method is called the caller). This way of exiting the method is called normal completion exit (Normal Method Invocation Completion). Another way to exit is that an exception is encountered during the execution of the method, and the exception is not handled in the method body. Whether it is the exception generated within the Java virtual machine or the exception generated by using the athrow bytecode instruction in the code, as long as no matching exception handler is found in the exception table of this method, the method will exit. This way of exiting the method is called exception completion exit (Abrupt Method Invocation Completion). A method exiting with an exception completion exit does not generate any return value to its upper-level caller.

No matter which exit method is adopted, after the method exits, the program needs to return to the location where the method was called before the program can continue to execute. When the method returns, it may need to save some information in the stack frame to help restore the execution state of its upper method. In general, when the method exits normally, the value of the caller's PC counter can be used as the return address, and this counter value is likely to be saved in the stack frame. When the method exits abnormally, the return address is determined by the exception handler table, and this information is generally not saved in the stack frame. The process of exiting a method is actually equivalent to taking the current stack frame off the stack, so the operations that may be performed when exiting are: restoring the local variable table and Operand stack of the upper method, pressing the return value (if any) into the Operand stack of the caller stack frame, adjusting the value of the pc counter to point to an instruction after the method call instruction, and so on.

Understanding stack frame with javap command

There is a large text introduction above, but it is still not easy to understand. Let's use the javap command to analyze the operation instructions, local variable table, Operand stack and so on.

Javap is an anti-parsing tool that comes with jdk. Its function is to inversely parse out the corresponding code area (assembly instruction), local variable table, exception table and code line offset mapping table, constant pool and other information of the current class according to the class bytecode file. Here are the instructions for its usage:

D:\ wyonline\ myworkspaces\ framework\ Test\ bin\ com\ wkp\ jvm > javap usage: javap, possible options include:-help-- help -? Output this usage message-version version information-v-verbose output additional information-l output line number and local variable table-public shows only public classes and members-protected shows protected / public classes and members-package display Show package / protected / public classes and members (default)-p-private shows all classes and members-c disassembles the code-s output internal type signature-sysinfo shows the System Information (path Size, date, MD5 hash)-constants displays the final constant-classpath specifies where to find user class files-cp specifies where to find user class files-bootclasspath overrides the location of boot class files

Let's write a simple Java program:

Package com.wkp.jvm; public class Math {public static final Integer CONSTANT=666; public int compute () {/ / A method corresponds to a stack frame memory area int axiom 3; int baud 5; int c = (aquib) * 10; return c } public static void main (String [] args) {Math math=new Math (); math.compute ();}}

Then go to the directory where Math.class is located and execute the command javap-c Math.class > Math.txt, disassemble the Math.class bytecode file and output it to the Math.txt file:

Then we look at the contents of Math.txt as follows: we focus on analyzing the instructions in the compute method, and I added notes after the internal instructions (here I refer to the previous section of "JVM bytecode instruction set and its introduction", if you are interested, you can take a look at it), the stack in the annotation refers to the Operand stack in the stack frame, and the local variable table refers to the local variable table.

Compiled from "Math.java" public class com.wkp.jvm.Math {public static final java.lang.Integer CONSTANT; static {}; Code: 0: sipush 666 3: invokestatic # 10 / / Method java/lang/Integer.valueOf: (I) Ljava/lang/Integer; 6: putstatic # 16 / / Field CONSTANT:Ljava/lang/Integer; 9: return public com.wkp.jvm.Math () Code: 0: aload_0 1: invokespecial # 21 / / Method java/lang/Object. "": () V 4: return public int compute () Code: 0: iconst_3 / / push 3 of int type to the top of stack 1: istore_1 / / push the value of int type (3 above) off the stack and store it in the second local variable 2: iconst_5 / / push 5 of int type to top 3: Istore_2 / / unstack the int type value at the top of the stack and store it in the third local variable 4: iload_1 / / push the second int local variable (3 above) to the top of the stack 5: iload_2 / / the third int local variable (5 above) ) push to the top of the stack 6: iadd / / push the top two int values out of the stack Then add and push the result to the top of the stack 7: bipush 10 / / push the constant value 10 to the top of the stack 9: imul / / push the top two int values out of the stack Then multiply and push the result onto the top of the stack 10: istore_3 / / take the int type value (the product above) off the stack and store it in the fourth local variable 11: iload_3 / / push the fourth local variable of type int to the top of the stack 12: ireturn / / return int from the current method Type value public static void main (java.lang.String []) Code: 0: new # 1 / / class com/wkp/jvm/Math 3: dup 4: invokespecial # 33 / / Method "": () V 7: astore_1 8: aload_1 9: invokevirtual # 34 / / Method compute: () I 12: pop 13: return}

The following is a brief illustration of the local variable table and Operand stack during the instruction operation in the above compute method:

Let's take a look at the first line 0: iconst_3 / / push 3 of type int to the top of the stack, and you can see that figure 3 below has been stacked to the top of the Operand stack.

Let's take a look at the second line 1: istore_1 / / take the int type value at the top of the stack (the 3 above) off the stack and store it in the second local variable, and then store the 3 out of the stack in the above figure and then in the second location in the local table, as shown in the following figure:

The third and fourth lines are similar to the above one or two lines of instructions, and the fourth line of instructions is executed as follows:

In the fifth and sixth lines, 4: iload_1 / / pushes the second int local variable (3 above) to the top of the stack; 5: iload_2 / pushes the third int local variable (5 above) to the top of the stack, pressing 3 and 5 of the local variable table into the top of the stack in turn, as shown in the following figure:

Then the seventh line performs the iadd operation, adding the two int type data 5 and 3 at the top of the stack, pressing the resulting sum onto the top of the stack and getting the following result:

The subsequent instruction operation is similar to the above, and after executing the iload_3 instruction on line 12, you will get the following figure:

Information about the local variable table can also be seen in the following figure through the javap-l command, as well as through the jclasslib in Idea.

LocalVariableTable represents the information of the local variable scale:

Public int compute (); LineNumberTable: line 8: 0 line 9: 2 line 10: 4 line 11: 11 LocalVariableTable: Start Length Slot Name Signature 0 13 0 this Lcom/wkp/jvm/Math; 2 11 1 an I 4 9 2 b I 11 2 c I

We can also see more information through javap-c Math.class > Math.txt: we can see that # 34 in 9: invokevirtual # 34 / / Method compute: () can find # 34 = Methodref # 1.room35 / / com/wkp/jvm/Math.compute: () in the constant pool, that is, the symbolic reference of the method. The runtime parses the memory address of the method's execution instruction through the symbolic reference, which is actually a dynamic connection.

Classfile / D:/wyonline/myworkspaces/framework/Test/bin/com/wkp/jvm/Math.class Last modified 2019-8-24 Size 761bytes MD5 checksum be0cdf4bcd037929d3fe0af86d44a837 Compiled from "Math.java" public class com.wkp.jvm.Math minor version: 0 major version: 52 / / Magic number flags: ACC_PUBLIC ACC_SUPERConstant pool: / / constant pool # 1 = Class # 2 / / com/wkp/jvm/Math # 2 = Utf8 com/wkp/jvm/Math # 3 = Class # 4 / / java/lang/Object # 4 = Utf8 java/lang/Object # 5 = Utf8 CONSTANT # 6 = Utf8 Ljava/lang/Integer # 7 = Utf8 # 8 = Utf8 () V # 9 = Utf8 Code # 10 = Methodref # 11.13 / / java/lang/Integer.valueOf: (I) Ljava/lang/Integer # 11 = Class # 12 / / java/lang/Integer # 12 = Utf8 java/lang/Integer # 13 = NameAndType # 14 valueOf: (I) Ljava/lang/Integer; # 14 = Utf8 valueOf # 15 = Utf8 (I) Ljava/lang/Integer # 16 = Fieldref # 1.Article 17 / / com/wkp/jvm/Math.CONSTANT:Ljava/lang/Integer; # 17 = NameAndType # 5 CONSTANT:Ljava/lang/Integer 6 / / # 18 = Utf8 LineNumberTable # 19 = Utf8 LocalVariableTable # 20 = Utf8 # 21 = Methodref # 3.room22 / / java/lang/Object. "": () V # 22 = NameAndType # 20 java/lang/Object 8 / "": () V # 23 = Utf8 this # 24 = Utf8 Lcom/wkp/jvm/Math # 25 = Utf8 compute # 26 = Utf8 () I # 27 = Utf8 a # 28 = Utf8 I # 29 = Utf8 b # 30 = Utf8 c # 31 = Utf8 main # 32 = Utf8 ([Ljava/lang/String ) V # 33 = Methodref # 1.V22 / / com/wkp/jvm/Math. "": () V # 34 = Methodref # 1.Zong35 / / com/wkp/jvm/Math.compute: () I # 35 = NameAndType # 25VIEV 26 / / compute: () I # 36 = Utf8 args # 37 = Utf8 [Ljava/lang/String # 38 = Utf8 math # 39 = Utf8 SourceFile # 40 = Utf8 Math.java {public static final java.lang.Integer CONSTANT; descriptor: Ljava/lang/Integer; flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL static {} Descriptor: () V flags: ACC_STATIC Code: stack=1, locals=0, args_size=0 0: sipush 666 3: invokestatic # 10 / / Method java/lang/Integer.valueOf: (I) Ljava/lang/Integer; 6: putstatic # 16 / / Field CONSTANT:Ljava/lang/Integer 9: return LineNumberTable: line 5: 0 LocalVariableTable: Start Length Slot Name Signature public com.wkp.jvm.Math () Descriptor: () V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial # 21 / / Method java/lang/Object. "": () V 4: return LineNumberTable: line 3: 0 LocalVariableTable: Start Length Slot Name Signature 050 this Lcom/wkp/jvm/Math; public int compute () Descriptor: () I flags: ACC_PUBLIC Code: stack=2, locals=4 Args_size=1 0: iconst_3 1: istore_1 2: iconst_5 3: istore_2 4: iload_1 5: iload_2 6: iadd 7: bipush 10 9: imul 10: istore_3 11: iload_3 12: ireturn LineNumberTable: line 8: 0 line 9: 2 line 10: 4 line 11: 11 LocalVariableTable: Start Length Slot Name Signature 0 13 0 this Lcom/wkp/jvm/Math 2 11 1 an I 4 9 2 b I 11 2 3 c I public static void main (java.lang.String []); descriptor: ([Ljava/lang/String ) V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=2 Args_size=1 0: new # 1 / / class com/wkp/jvm/Math 3: dup 4: invokespecial # 33 / / Method "": () V 7: astore_1 8: aload_1 9: invokevirtual # 34 / / Method compute: () I 12: pop 13: return LineNumberTable: line 15: 0 line 16: 8 line 17: 13 LocalVariableTable: Start Length Slot Name Signature 0 14 0 args [Ljava/lang/String 8 61 math Lcom/wkp/jvm/Math;} SourceFile: "Math.java" above is all the content of this article "sample Analysis of Java Virtual Machine Stack". Thank you for reading! I believe we all have a certain understanding, hope to share the content to help you, if you want to learn more knowledge, welcome to follow the industry information channel!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report