Where are primitive fields stored?
Primitive fields are stored as part of the object that is instantiated somewhere. The easiest way to think of where this is - is the heap. However, this is not always the case. As described in Java theory and practice: Urban performance legends, revisited:
JVMs can use a technique called escape analysis, by which they can tell that certain objects remain confined to a single thread for their entire lifetime, and that lifetime is bounded by the lifetime of a given stack frame. Such objects can be safely allocated on the stack instead of the heap. Even better, for small objects, the JVM can optimize away the allocation entirely and simply hoist the object's fields into registers.
Thus, beyond saying "the object is created and the field is there too", one cannot say if something is on the heap or on the stack. Note that for small, short-lived objects, it's possible that the 'object' won't exist in memory as such and may instead have its fields placed directly in registers.
The paper concludes with:
JVMs are surprisingly good at figuring out things that we used to assume only the developer could know. By letting the JVM choose between stack allocation and heap allocation on a case-by-case basis, we can get the performance benefits of stack allocation without making the programmer agonize over whether to allocate on the stack or on the heap.
Thus, if you have code that looks like:
void foo(int arg) {
Bar qux = new Bar(arg);
...
}
where the ...
doesn't allow qux
to leave that scope, qux
may be allocated on the stack instead. This is actually a win for the VM because it means it doesn't need to ever be garbage collected - it will disappear when it leaves the scope.
More on escape analysis at Wikipedia. For those willing to delve into papers, Escape Analysis for Java from IBM. For those coming from a C# world, you may find The Stack Is An Implementation Detail and The Truth About Value Types by Eric Lippert good reads (they're useful for Java types also, as many of the concepts and aspects are the same or similar). Why do .Net books talk about stack vs heap memory allocation? also goes into this.
On the whys of the stack and the heap
On the heap
So, why have the stack or the heap at all? For things that leave scope, the stack can be expensive. Consider the code:
void foo(String arg) {
bar(arg);
...
}
void bar(String arg) {
qux(arg);
...
}
void qux(String arg) {
...
}
The parameters are part of the stack too. In the situation that you don't have a heap, you would be passing the full set of values on the stack. This is fine for "foo"
and small strings... but what would happen if someone put a huge XML file in that string? Each call would copy the entire huge string onto the stack - and that would be quite wasteful.
Instead, it is better to put the objects that have some life outside of the immediate scope (passed to another scope, stuck in a structure that someone else is maintaining, etc...) into another area which is called the heap.
On the stack
You don't need the stack. One could, hypothetically, write a language that doesn't use a stack (of arbitrary depth). An old BASIC that I learned on in my youth did so, one could only do 8 levels of gosub
calls and all variables were global - there was no stack.
The advantage of the stack is that when you have a variable that exists with a scope, when you leave that scope, that stack frame is popped. It really simplifies what is there and what isn't. The program moves to another procedure, a new stack frame; the program returns to the procedure, and you've back in the one that sees your current scope; the program leaves the procedure and all the items on the stack are deallocated.
This really makes life easy for the person writing the runtime for the code to use a stack and a heap. There are simply many concepts and ways of working on the code, allowing the person writing the code in the language to be freed of thinking of them explicitly.
The stack's nature also means it can't become fragmented. Memory fragmentation is a real problem with the heap. You allocate a few objects, then garbage collect a middle one, and then try finding space for the next large one to be allocated. It's a mess. Being able to put things on the stack instead means that you don't have to deal with that.
When something is garbage collected
When something is garbage collected, it's gone. But it is only garbage collected because it's already forgotten about - there are no more references to the object in the program that can be accessed from the current state of the program.
I'll point out that this is a very large simplification of garbage collection. There are many garbage collectors - even within Java, you can tweak the garbage collector by using various flags (docs). These behave differently and the nuances of how each one does things is a bit too deep for this answer. You may wish to read Java Garbage Collection Basics to get a better idea of how some of that works.
That said, if something is allocated on the stack, it is not garbage collected as part of System.gc()
- it is deallocated when the stack frame pops. If something is on the heap, and referenced from something on the stack, it will not be garbage collected at that time.
Why does this matter?
For the most part, it's tradition. The text books written and compiler classes and various bits documentation make a big deal about the heap and the stack.
However, virtual machines of today (JVM and similar) have gone to great lengths to try to keep this hidden from the programmer. Unless you are running out of one or the other and need to know why (rather than just increasing the storage space appropriately), it doesn't matter too much.
The object is somewhere and it's in the place where it can be accessed correctly and quickly for the appropriate amount of time that it exists. Whether it's on the stack or the heap - it doesn't really matter.