So I know that there exists 2 memory areas: Stack and Heap.
I also know that if you create a local variable it will live in the Stack, not in the heap. Stack will grow as we push data into it as in:
Now I will try to pass the confusion I am having to you:
For example this simple Java Code:
public class TestClass {
public static void main(String[] args) {
Object foo = null;
Object bar = null;
}
}
is translated into this byte code:
public static void main(java.lang.String[]);
Code:
Stack=1, Locals=3, Args_size=1
0: aconst_null
1: astore_1
2: aconst_null
3: astore_2
4: return
LineNumberTable:
line 5: 0
line 6: 2
line 7: 4
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 args [Ljava/lang/String;
2 3 1 foo Ljava/lang/Object;
4 1 2 bar Ljava/lang/Object;
where by definition acons_null is:
push a null reference onto the stack
and astore_1 is:
store a reference into local variable 1
The confusion I am having is, we pushed the foo into stack, then we stored it in the stack again? What is meant by storing a reference in a local variable? Where does that local variable live? The same stack we pushed the foo into or are these seperate Stacks?
Now at that point, if I call a method on the first object I pushed into the stack, since the stack pointer is pointing to the last element I pushed, how will it be processed?
You should look at structure of Java stack frame.
A java stack frame contains 3 things :
So,
push a null reference onto the stack
--> pushes the reference onto the operand stack.store a reference into local variable 1
--> stores the reference into slot 1 of local variable tableThere exists one stack per thread in the JVM. Each stack is composed of several frames: each method invocation creates a new frame, and when the method invocation is done, the frame is destroyed.
Within a stack frame there are two areas :
Depending on the JVM implementation, they may or may not be contiguous in memory. Logically they are two separate sections of the stack frame.
As explained in the description of
aconst_null
, theaconst_null
instruction pushes thenull
object reference onto the operand stack.And as explained in the description of
astore_<n>
(wheren
could be 0, 1, 2 or 3):So in your example, the statement
Object foo = null
translates to the following:null
(a special reference that points to "nothing") onto the top of the operand stack.foo
.Same steps are done for
Object bar = null
except thatnull
is stored in the local variable at index 2.Source: Java Virtual Machine Specification (See this section).
You can think of the operand stack as temporary variables. It's local to each method call, and its size can be determined at compile time.
If you want to do anything with any kind of variables (local variables, static variables, or non-static variables), you do it via the operand stack. Java Bytecode instructions work mainly only with the operand stack.
For example,
foo = bar
would correspond toaload_2
andastore_1
, which simply mean push the value of local variable 2 onto the operand stack and pop whatever on top of the operand stack to local variable 1if (foo == null) ...
would correspond toaload_1
andifnonnull 5
, where the latter tells the JVM: if whatever on top of the operand stack is not null, jump to the next 5 instruction offsets; otherwise, continue to the next instruction.int x = args.length
would correspond toaload_0
,arraylength
,istore_3
, which mean push local variable 0, pop the array on top the operand stack and push its length back, pop the integer and store it in local variable 3iadd
,isub
,imul
,idiv
pop two integer values from the operand stack and push the result backputstatic
/getstatic
pops/pushes to/from static variablesputfield
/getfield
pops/pushes to/from non-static variablesIt's the same stack.
Or at least you can think of it as the being the same stack, it actually depends on the jvm implementation.
In a simple jvm
When a method is called it reserves space for the local variables on the stack. It basically increments the stack pointer to open space for it's local variables. The method's parent object (if instance method) and the method's arguments are the first locals.
To assign something from the stack to a local var is to copy from the top of the stack to some a nearby address, a few positions before, in the same memory region.
During
astore 1
in your example: