Java bytecode angle diagram i ++ and ++ i

When learning C language from undergraduate time, we were taught:

++ i is incremented first and then assigned

i ++ is assigned first and then incremented

Recently I am looking at concurrent programming of jvm virtual machine and java. I hope to introduce it from the perspective of bytecode.

In fact, many people have written this detail, but I still want to make a little synthesis and describe it in a more popular and intuitive way.

To read this article, you need to have a certain understanding of the Java Virtual Machine (Java Virtual Machine, JVM), at least you need to understand the meaning and method of bytecode instructions (bytecode instructions) in the JVM virtual machine stack (JVM Stack) execution process . For details, please refer to "Understanding Java Virtual Machine: Advanced Features and Best Practices of JVM".

After reading the following, you will understand (rather than memorize) the running results of the following code:

The code snippet is taken from the difference between i ++ and ++ i in java

int a = 0;
for(int i = 0; i < 99; i++){
    a = a++;
}
System.out.println(a); // 输出0
int b = 0;
for (int i = 0; i < 99; i++) {
    b = ++ b;
}
System.out.println(b); // 输出99
int a = 0;
int b = 0;
for (int i = 0; i < 99; i++) {
	a = a++;
	b = a++;
}
System.out.println(a); // 输出99
System.out.println(b); // 输出98

Bytecode and virtual machine

The bytecode instruction in java is equivalent to the machine code in c / c ++.

The developer writes the source file (* .java) and compiles the source file into a class file understandable by the JVM through the javac command, which contains the constants, interfaces, fields, methods and other information of the class.

For each method, it contains line number table (LineNumber Table), local variable table (LocalVariable Table), byte code, exception table (Exception Table) and other information.

Use the javapcommands under the JDK or jclasslib bytecode viewer software to view the bytecode information.

Each method is composed of several bytecode instructions, which will affect the local variables (Local Variables) and operand stack (Operand Stack) of the current method . Note that only one local variable table and operand stack are maintained in a method body; the operand stack holds the value of the current variable, and the elements in it are only performed through two operations, push and pop.

For the specific instruction meaning, please refer to the instructions in the official JVM specification of Oracle Chapter 6. The Java Virtual Machine Instruction Set

Command name Changes in the operand stack description
iconst_<i> i Push Put an integer on the istack
istore_<i> Top element pops (pop) Pop the integer at the top of the stack and assign it to ithe element with index in the local variable table
iload_<n> Push the middle element of the local variable table Put nthe element with index in the local variable table on the stack
iinc i by n > No change iAdd to the element with index in the local variable tablen
iadd Pop two elements at the top of the stack and return one element (pop, pop, push) Pop two elements consecutively from the top of the stack, and put the result of adding them back onto the stack
getstatic<i> Put symbol references on the istack Put symbol references on the istack
invokevirtual<n> ..., objectiveref, [arg1, [arg2...]] -> ... (pop, pop, pop ,...) Call method n of objectref in the stack, and use arg1, arg2 as the parameters of the method

It is important to note here that the iincauto-increment instruction directly accumulates the elements of the local variable table instead of on the stack.

If readers who don't understand the instruction set may still be in the fog, it doesn't matter, the following will introduce it with a graphical method.

Separate i ++ and ++ i operations

If there is only i++or ++i, such as:

int i = 0;
i++;
int i = 0;
++i;

The bytecode instructions are the same, both are:

0 iconst_0
1 istore_1
2 iinc 1 by 1
5 return

Separate i ++ and ++ i

The figure describes the bytecode execution mechanism of the above code: line 0 adds 0 to the top of the operand stack; line 1 stores the element at the top of the stack to the index 1 of the local variable table, that is i; Behavior increment operation. Note that there is no stack operation here. In the local variable table, directly add the variable with index 1 to 1; the fifth behavior is the return statement, and there is no return value.

Through the above introduction, the reader may probably understand the execution principle of bytecode. Note that the bytecode is the same whether it is i++or ++inot. If we write for(int i = 0; i<len; i++/++i)can

i ++ and ++ i assignment

Let's take a look at i++the case of assignment:

int i = 0;
i = i++; 
System.out.println(i); // i = 0

How do you understand that when you assign a value, you istill get 0? The byte code is as follows (excluding print output statements):

0 iconst_0
1 istore_1
2 iload_1
3 iinc 1 by 1
6 istore_1
7 return

i ++ assignment byte code flow

It can be seen from the bytecode instruction that when it i++;becomes i=i++;, line 2 loadfirst icopies the value to the top of the stack through the command , and then iincrements on line 3 , but at this time the increment is executed in the local variable table 0->1, However, the value on the stack remains unchanged; then on line 6, the value at the top of the stack is again assigned to index 1 in the local variable table, that is, to the variable i, so it iis still 0.

Such results are still i++assigned first, and then increase without conflict. iI first copied a value on the top of the stack and incremented it, but then I assigned the top of the stack to the left end item ijust to be myself.

Next, look at the ++iassignment:

int i = 0;
i = ++i;
System.out.println(i); // i = 1

The byte code is as follows (excluding print output statements):

0 iconst_0
1 istore_1
2 iinc 1 by 1
5 iload_1
6 istore_1
7 return

1586612151133

It can be seen from the bytecode that in i=++ithe second irow after the increment, the local variable table becomes 1, then the fifth line loads i from the variable table, and the sixth line is assigned back. At the same time, i=++i;this line of statement will also prompt the message "The assignment to variable i has no effect", meaning that such an assignment is meaningless. As explained by the bytecode, we load ithe value and assign it back.

From the above comparison, we can see that in the case of assignment:

i++First load the original value into the stack, increment the variable table, and then assign the value in the stack to the corresponding variable;

++iFirst increase the variable table, load it into the stack, and then assign the value in the stack to the corresponding variable.

Examples of other situations

Example 1 (from the interpretation of i ++ and ++ i with java bytecode ):

int i = 0;
int j = 0;
j = i++ + i++;
System.out.println(j); //输出j=1
System.out.println(i); //输出i=2

Why j = 1, i = 2? The bytecode is interpreted as:

 0 iconst_0
 1 istore_1
 2 iconst_0
 3 istore_2
 4 iload_1
 5 iinc 1 by 1
 8 iload_1
 9 iinc 1 by 1
12 iadd
13 istore_2
14 return

Example 1 byte code flow

Seen from bytecode, 0-3 initialization behavior iand jvalues of each calculated i++, the first is ithe old value is loaded into the stack and then iincrement. At the first time , the local variable table i++in the stack i=0is i=1; at the second i++time, the stack is loaded after the increment i, namely 1, and then iincremented again in the variable table at this time i=2. The last i++ + i++value to be calculated , so the iaddinstruction will pop up the two elements in the stack and add 1 to it, and then return it to the top of the value stack. Finally, the istoreinstruction assigns the value to the joperation and the operation ends. Therefore, we finally got the i=2,j=0+1=1result.

Example 2:

int i = 0;
int j = 0;
j = i++ + i++ + i++;

The bytecode file is as follows:

 0 iconst_0
 1 istore_1
 2 iconst_0
 3 istore_2
 4 iload_1
 5 iinc 1 by 1
 8 iload_1
 9 iinc 1 by 1
12 iadd
13 iload_1
14 iinc 1 by 1
17 iadd
18 istore_2
19 return

The result of this operation is i=3,j=3.

According to the previous case, it can be deduced: i ++ has been calculated 3 times, therefore i=3;j=0 + 1 + 2 =3

Example 3: Viewing i ++ and ++ i from the perspective of JVM

int i = 0;
i = i++ + ++i; //i最终为2

The bytecode is:

 0 iconst_0
 1 istore_1
 2 iload_1
 3 iinc 1 by 1
 6 iinc 1 by 1
 9 iload_1
10 iadd
11 istore_1
12 return

Example 3 byte code flow

I will not write the specific execution process, I believe everyone can understand it by looking at the picture.

summary

I have skipped the example at the beginning, and if you push it carefully, you can certainly introduce it.

Before I discuss i++or ++isome other questions like the same boring "fennel" several writing, but the look on until the Java virtual machine and concurrency only after really understand why i++the operation does not have the atomicity, it is because in the instruction code It is not an instruction, but has three operations of reading, adding and assigning.

Guess you like

Origin www.cnblogs.com/TianYuanSX/p/12682824.html
I
"I"
I: