This question may be a silly one or may be a duplicate. I am confused with how the variables are retrieved from stack when program is referring to that variable. An object is stored in heap and the location is stored in the reference variable and the reference variable containing the heap address itself is stored in stack. But how JVM figure out that reference variable is stored in what location in stack.
Lets consider the example just to make clear what I am confused about.
Class Test {
public void test() {
Object a = new Bar();
Object b = new Foo();
System.out.println(a);
}
}
Lets say method test() is getting executed. So stack will be allocated for test().
Now when the line 'Object a = new Bar();' is executed , Bar object will be created in Heap and the actual variable 'a', whose value is the address location of the Bar object , will be stored in stack of test().
Again on the line 'Object b = new Foo();' same thing happens. Foo object will be created in Heap and the actual variable 'b' ,whose value is the address location of the Foo object, will be stored in stack of test().
Now when he line 'System.out.println(a);' is executed , how does JVM know from which location in the stack , the value of 'a' should be retrieved. Means what links the variable 'a' and its location in the stack ?
The stack is used for dynamic memory allocation, and local variables are stored at the top of the stack in a stack frame. A frame pointer is used to refer to local variables in the stack frame. Figure 110: Stack frame before and after the LINK instruction.
Local variables are created in the stack. Instance variables are created in the heap & are part of the object they belong to. Reference variables are created in the stack.
A Java Virtual Machine stack stores frames (§2.6). A Java Virtual Machine stack is analogous to the stack of a conventional language such as C: it holds local variables and partial results, and plays a part in method invocation and return.
All Local Variables and method calls gets stored in Stack , But Instance variables and Objects reside inside the Heap .
You're almost there, there's just one missing link in your understanding.
Local variables (or the references to the object stored in a local variable, if we're talking about non-primitive types) are actually stored in a local variable table, not on the operand stack. They're only pushed onto the stack when they are to be used by a call.
(What is confusing is that the local variable table itself is also stored on a stack, but that's a separate stack from what the bytecode uses for operands. From the bytecode's perspective it is a real table, with fixed size and freely indexable.)
You can use javap
to look at what bytecode is generated from your code. What you'll see is something like this:
public void test();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=3, locals=3, args_size=1
0: new #2 // class Test$Bar
3: dup
4: invokespecial #3 // Method Test$Bar."<init>":()V
7: astore_1
8: new #4 // class Test$Foo
11: dup
12: invokespecial #5 // Method Test$Foo."<init>":()V
15: astore_2
16: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
19: aload_1
20: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
23: return
}
First of all, what is this line?
stack=3, locals=3, args_size=1
It is metadata that tells the JVM that this method has an operand stack no deeper than 3 entries, 3 local variables and takes 1 argument. But surely that isn't right, our method takes no argument and clearly only has 2 local variables!
The answer to this is that non-static methods always have a "0th argument": this
. This explains the argument count, and leads us to the next important discovery: arguments of a method are stored in the local variable table too. So our table will have entries 0,1,2, with 0 containing this
at the start and 1 and 2 uninitialised.
With that out of the way, let's look at the code! First up it's lines 0-7
:
new
opcode creates a new instance of Bar
and stores the reference on the stack.dup
creates a copy of the same reference on the top of the stack (so you've got two copies now sitting there)invokespecial #3
calls the constructor of Bar
and consumes the top of the stack. (now we've only got one copy left)astore_1
stores the remaining reference in local variable number number 1
(0
is for this
in this case)This is what Object a = new Bar();
has been compiled into. Then you get the same for Object b = new Foo();
(lines 8-15
).
And then comes the interesting bit, from line 16
:
getstatic #6
pushes the value of System.out
on the stackaload_1
pushes local variable number 1 (a
) on the stack tooinvokevirtual #7
consumes both entries, calling println()
on System.out
with a
as its input parameter.If you want to delve into it deeper, or you just want to point out my mistakes, the official reference for all of the above is here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With