![]() |
|||
![]() ![]() |
![]() |
|
![]() |
So far, weve counted three local variables in positions 1 through 3. However, in a non-static method, the zeroth position always holds a reference to the current object. Therefore, there are four total local variables in positions 0 through 3.
In this example, the source code contains three local variables a, b, and cand the byte code contains four local variables. The number of local variables in the .java source code is not necessarily the same as the number of local variables used by the byte code. The byte code may have more local variables if it needs temporary storage, or it may have fewer if some variables can be reused. The other thing to note is that the names of the local variables are completely lost in compilation; only their types are known. When the method is first called, an array of four words is allocated to hold the local variables. The zeroth element of the array is a reference to this object. The first element of the array corresponds to the int variable a; the second element of the array correspondents to the int variable b; and the third element of the array correspondents to the int variable c. The stack is initially empty. Figure 5-1 demonstrates.
The zeroth instruction is iconst _ 4. The const family of instructions push values onto the operand stack. This instruction begins with the letter i, so it pushes an int value. It ends with _ 4, so the value pushed is four. Thus, after this instruction has executed, the operand stack is one word high, and the int 4 is on the top of the stack. Figure 5-2 shows the state of the local variable array and the operand stack after this instruction is executed.
The first instruction is istore _ 1. The store family of instructions pop values from the operand stack and store them in the local variable array. This instruction begins with the letter i, so it stores an int value. It ends with _ 1, so the value is stored in local variable 1. Therefore, 4 is popped off the stack and stored in local variable 1. After this instruction has executed, the operand stack is empty. Figure 5-3 shows the state of the local variable array and the operand stack after this instruction is executed.
The second instruction is iconst _ 2. This pushes the int 2 onto the stack. Figure 5-4 shows the state of the local variable array and the operand stack after this instruction is executed.
The third instruction is istore _ 2. It pops the top of the stack and stores it in local variable 2. After this instruction has executed, the operand stack is empty. Figure 5-5 shows the state of the local variable array and the operand stack.
The fourth instruction is iload _ 4. The load family of instructions pushes values from the local variable array onto the operand stack. This instruction begins with the letter i and ends with _ 4, so it pushes the int 4. Unlike popping a value from the stack, loading a value from the local variable array does not remove it from the array. Local variable 1 still has the value 4 after this instruction is executed. Figure 5-6 shows the state of the local variable array and the operand stack.
The fifth instruction is iload _ 2. This pushes the second local variable onto the stack. After this instruction has executed, the operand stack is two words high, and the int 2 is on the top of the stack. Figure 5-7 shows the state of the local variable array and the operand stack.
The sixth instruction is iadd. The add family of instructions pops two values from the operand stack, adds them, and pushes the result back onto the stack. This instruction begins with the letter i, so it adds ints. After this instruction has executed, the operand stack is one word high, and the int 6 is on the top of the stack. Figure 5-8 shows the state of the local variable array and the operand stack.
The seventh instruction is istore_ 3. This pops the top of the stack and stores it in local variable 3. After this instruction has executed, the operand stack is empty. Figure 5-9 shows the state of the local variable array and the operand stack.
The eighth instruction is iload_ 3. This pushes the value 6 from local variable 3 onto the top of the stack. After this instruction has executed, the operand stack is one word high, and the int 6 is on the top of the stack. Figure 5-10 shows the state of the local variable array and the operand stack.
The ninth and final instruction in this method is ireturn. This instruction pops the int from the top of the stack and returns it to the method that calls this method. Theres no picture here because this instruction destroys the frame, so there are no more local variables or stack words after this instruction is executed. You may have noted that the seventh and eighth instructions werent strictly necessary. The value 6 was on the top of the stack after instruction 6 and could have been returned then. This would be equivalent to rewriting the .java source code without the intermediate variable c, like this: public int six() { int a = 4; int b = 2; return a+b; } An optimizing compiler might have noticed this and omitted the seventh and eighth instructions. A very good optimizing compiler could have noticed that this method uses only constants and always returns 6. It would have thus rewritten the source code like this: public int six() { return 6; } In fact, this is exactly what javac -O does. The following is the byte code emitted by javac with the -O flag to indicate that it should perform optimization. public int six() { 0 bipush 1 6 2 ireturn }
|
![]() |
|