Stack and Heap are two areas of the memory (RAM) that Java uses to store objects and variables created in a running program.
In this section, we will learn what the Stack and Heap memories are and how they work in Java.
Java Stack and Heap Prerequisite Analogy
To better understand the Stack and Heap, let’s first look at an analogy:
Let’s say we’re reading an article and on the page 10, there’s a reference to another article that needs to be read and that is a prerequisite to the rest of this first-article.
So we stop at the page 10 of the first article, find the second one and bring it on top of the first article and read.
Note: basically we’ve created a stack of two articles.
While reading the second article, we realize that at the page 13 there’s another reference to an article and we need to read that article first before proceeding to the rest of this second article.
So we find the third article and read it.
Note: now there are three articles sacked on top of each other.
Fortunately, there’s no reference in the third article and we read all of it and so it’s now off the stack.
Now we’re back to the second article right at page 13 and keep reading the rest of the article.
The second article also finished with no reference to another external resource, and so it’s off the stack as well.
Back to the first article, we keep reading from the page 10 (where the second article showed up) and as we progressed, in the page 18 there’s another reference to another article.
So again we find the reference and start reading it.
Note: while we’re reading this reference article, the first one is at the page 18 and waiting for its turn (after the referenced article is finished).
After a while, the reference-article is finished and off the stack. So we’re back to the first article right in the page 18 and keep reading to the end of the article.
Fortunately, there wasn’t any other external reference, and the first article finished as well.
Alright, now let’s focus on Java and see how this story is related to Java memory (more specifically, the Stack memory).
In computers in general section, we mentioned that the CPU is at the heart of a computer and is in charge of running programs.
Basically, the CPU reads every instruction of a program (every declaration, method calling, assignments etc.) And run them one by one from top to bottom. You can think of a program as the article in our story.
Like any article that has a starting point and we read from this point, page by page, until it is finished, a program has an entry as well and the CPU starts from there.
The `main()` method is considered being the entry point of a program in Java.
Note: before a Java program can run, the JVM should already be loaded in the memory. Inside the JVM, we have `class loader`. Class loader is in charge of loading the compiled .class files of a program into the memory that is managed by the JVM.
This means the JVM divides the memory space into segments and each part of a program will be allocated in each of these segments.
After the program is loaded correctly in the memory, another component of the JVM which is called the `execution engine` will find the `main()` method of the program and from there it’ll start to translate each instruction of the program and send it to the CPU for the execution.
Basically, the `execution engine` is the middleman or the translator here. It reads instructions, translates it and sends it to the CPU for the execution. After the execution is done, it will read another instruction and run the same process again until there’s no instruction left in the program. In that case the program is ended and will be removed from the memory.
Note: The execution of instructions of a program is statement by statement from top to bottom. This means the JVM’s execution engine will not start from the first line of the `main()` method and then randomly jump to another line! No, it will read the first line, then the next one below it and so on…
Let’s see an example:
public class Simple { public static void main(String[] args) { int a = 20 ; int b = 30; int c = 40 ; int d = 50; int res = (a+b) * d; // (20+30) * 50 } }
In this example, the execution engine will start from the first statement which is declared and initializing the `a` variable, then move on to the next statement, which is again declaring and initializing a variable named `b`. The next two statements are also declaring and initializing two variables named `c` and `d` and assigning the values 40 and 50 to these variables, respectively.
Then the execution engine will move on to the fifth statement, which is declaring another variable named `res` and assigning the result of the expression `(a+b) * d;` to this variable.
Notes:
- Spaces are removed at compile time.
- When we say a line of instruction, it doesn’t mean if a program has 10 lines then there’re 10 instructions in that program!
For example, if we had:
public class Simple { public static void main(String[] args) { int a = 10 ;int b = 20;int c = 30 ;int d = 40; int result = (a+b) * d; // (10+20) * 40 } }
The ` int a = 10 ;int b = 20;int c = 30 ;int d = 40;` is 4 instructions for the CPU to run!
Also, if we had a line of instruction like:
int a = 10 ;
This is still considered one line of instruction (or simply one statement) even though it took 5 editor’s line!
Basically, semicolon is one factor of separating lines of instructions from each other.
Java Memory Management
In this section, we explain how memory works and the segmentation that Java applies to the memory.
Memory:
A program at first is stored in the hard-disk. When we want to run this program, the class loader will move it from the hard-disk to the memory (the part of the memory that is managed by the JVM) so that the execution engine can take the instructions of this program, translate it and send it to the CPU for execution.
This is like we’re bringing an article from a library and place it on a table and start to read. In this analogy, the library is the hard-disk, the article is the program, the table is the memory (RAM) and the CPU is actually the person that wants to read the article.
The JVM divides the memory into multiple segments and three of them that are related to this section are:
Stack Memory in Java
Stack is the region of the memory that a method takes place when it is being called.
To use an analogy, stack would be the region on the table that we put an article there to read.
It is called stack, because when a `caller method` calls another method (`called-method`) it is like putting the `called-method` on top of the `caller method` so the execution of the instructions of the `caller method` will be stopped for a while and the CPU will move on to the `called method` to run its instructions. So while the `called-method` is running by the CPU, the `caller-method` is standing still.
This is like when we had one article and another reference came up so we stopped the first article and started to read the second one.
Stack Memory in Java Notes
- If one method called another method, the second one will take its own place in the stack memory and won’t replace the memory space of the first method in the stack. This means both methods will hold their local variables and data.
- But when the second method or any other method finished its work (all the instructions in the body of the method executed), the memory space allocated to this method is taken back and so any data in the body of the method will be destroyed after the method is ended.
- This means as long as a method is in the stack-memory, so are its local data (variables etc.)
- You should know that the stack memory space is a limited area and so we can’t store large amount of data in methods.
Java METASPACE
Metaspace is the memory segment that the JVM uses to store class metadata. Class metadata is any information that the JVM needs to work with a Java Class.
Other than the class metadata, static methods and static variables are also stored here.
Unless explicitly reclaimed by garbage collection, the data in METASPACE will stay there for as long as the program is running.
Heap Space in Java: What is Heap in Java?
The objects and arrays that we create in a program are stored in this segment of the memory and it is managed by the garbage collections.
Garbage collection is a program (part of the JVM) that runs automatically behind the scene and looks for those objects that no variable is pointing to them. If GC (Garbage Collection) found one, it’ll automatically remove it from the memory.
Java Heap Memory Notes:
- When we assign an object to a variable, only the memory address of the place where object is stored will be assigned to the variable. For this reason, we say the variable is referencing (pointing) to an object that is somewhere else. Check the reference & value section for depth explanation.
- Also check the garbage collection section to see how and why a variable might no longer point to an object.
In short Garbage Collection removes unwanted objects because the space of the Heap segment is finite and so those objects in the memory that are no-longer needed by the program should be removed to save space for other future objects that might be created later in that program.
Note: the objects and arrays that are in the heap segment will stay there for as long as the garbage collection does not remove them.
Example: Stack memory in Java
public class Simple { public static void main(String[] args) { int a = 10 ; int b = 20; int result = sum (a, b); System.out.println("The result is: "+ result); } public static int sum (int valueOne , int valueTwo){ return valueOne + valueTwo; } }
Output: The result is: 30
In the example above, considering that the entry point of Java programs is the `main()` method, the CPU will start by running the instructions within the body of this method.
Note: when the `main` method is called, a memory space in the stack is set aside for it and any local data (variables etc.) of this method will be stored in there. Basically, the `main()` method is the first method that comes to the stack.
Also, because this method is at the bottom of the stack, it will be the last one to leave as well, which in this case it is considered the end of the program.
The first two lines of instructions is to declare two variables named `a` and `b` and then assign the values 10 and 20 to these variables, respectively.
So these two variables are now the local variables of the `main()` method and are stored in the memory space allocated to the method in the stack-segment.
On the third line of instructions, things get more interesting!
First, the instruction is to declare a variable named `result`. So this variable will be declared and then it’s time to run the assignment part of this variable.
The instruction here is to call another method named `sum()`.
This is like we are at some page of an article and then we face a reference that is needed to be read first in order to be able to continue with the rest of the article.
So what happens here is that the first method `main` will stop executing the rest of its instructions by the CPU and the CPU will start to run the instructions within the body of `sum()` method.
Note: before this method gets the chance of running, part of the memory in stack will be allocated to this method and every local variable and their values in this method will be stored in the allocated memory space within the stack. (`int valueOne` and `int valueTwo` are the two variables that are considered to be the local variable of the `sum` method).
When we called the `sum` method in the body of `main` method, we put two arguments in the method and as explained in the method section these arguments will be the values assigned to each parameter of the method. So now the `valueOne` variable has the value 10 and `valueTwo` variable has the value 20 stored in their memory spaces.
Now the `sum` method is on top of the stack and its instructions is about to run while the `main` method is waiting for this one to finish its instruction execution.
Within the body of `sum` method there’s only one statement to be executed and that is returning the result of `valueOne + valueTwo` expression to the `caller method` which is the `main` via the `return` keyword.
The result is 30, and this value is returned to the `main` method and is assigned to the `result` variable.
At this moment, the memory space assigned to the `sum` method is taken back and the method no-longer exists in the stack.
The next line of instructions in the `main()` method is to call the `println()` method to send the combination of the value assigned to the `result` variable and another message to the output stream.
Note: the `println()` method will also take its own memory space in the stack and after that the CPU will start to run the instructions within the body of this method.
After the execution of the `println()` method is ended, its memory space is taken back and the execution will return to the `main()` method.
After that, there’s no instruction in the `main()` method to run.
At this stage, the `main` method reached to its end and the memory space allocated to this method in the stack will be released as well and of course this is equal to the end of our program. So the program will terminate at this point.
Difference between Heap and Stack Memory: Stack vs Heap
In short, `Heap` mainly concerns with objects and is the area of the memory that objects are stored. These objects will stay in the memory as long as there’s a reference to them. On the other hand, `Stack` is the region of the memory where the local variables of methods will be stored in. These local variables can live in the stack as long as the execution of their method is not done. But the moment a method is finished, those variables are off the stack and memory.
Also, the size of the Heap area is way more than the size of the Stack.