Hurry! Try our new Interactive Courses for FREE. 🥳   🚀

Java Stack and Heap Memory

Memory in Java is divided into two parts - the Stack and the Heap. This is done to ensure that our application efficiently utilizes memory.

In this tutorial, we will learn more about these two types of memories and the key differences between them.

Stack Memory

  • The stack memory is used for thread execution and static memory allocation. All primitive data resides in the stack memory and the references to other objects are also stored in it. It only contains local data that is available for the current thread.
  • Whenever a method is called from a Java application, then a new block for that method is added to the stack. This block will contain all the primitive data and references that are needed by the method.
  • Stack memory, as the name suggests, uses the Last-In-First-Out(LIFO) approach of the Stack data structure to add and remove method calls.
  • Stack memory is a lot faster than heap memory. It is also safer than heap memory because the data can only be accessed by the running thread. It is thread-safe and does not require synchronization as each thread gets its own Stack.
  • Allocation and deallocation of memory are done automatically. Whenever a method is called, then a new block for this method is added at the top of the Stack. And when the method finishes execution or returns, then the block and all the corresponding data are removed and the space becomes available for new method calls.
  • We may encounter the StackOverflowError if we run out of the Stack memory. We can write a simple program to simulate this error. If we repeatedly call a method, then these method calls will accumulate in the Stack, and eventually, we will get this error. In the following code, we recursively call the overflowError() method inside its definition and this leads to StackOverflowError.
public class JavaMemoryManagement
{
	public static void overflowError()
	{
		overflowError();
	}	
	public static void main(String[] args)
	{
		overflowError();
	}
}

Let's see an example to understand how memory is allocated in the stack.

In the code below, we are creating primitive integer data and char data in the main() method. This main() method will be pushed on the top of the stack. Then we are calling the print() method, which will again be added to the top of the stack. Inside the print() method, we are calling the printChar() method, and a block for this method is allocated on the stack. All these methods blocks in the stack will have the required data available to them.

public class JavaMemoryManagement
{
	public static void print(int i, char c)
	{
		System.out.println(i);
        printChar(c);
	}	
	public static void printChar(char c)
	{
		System.out.println(c);
	}	
	public static void main(String[] args)
	{
		int i = 5;
		char c = 'a';
		print(i, c);
	}
}

The final stack memory is shown in the image below.

Stack memory snapshot after method calls

Heap Memory

  • The heap memory is the place where all class instances or objects are allocated memory. As discussed above, the references to these objects are stored in the Stack memory. All dynamic memory allocation takes place in the heap. Heap memory is also globally accessible and any thread can access the memory.
  • Unlike the Stack memory, which uses the stack data structure, the heap data structure is in no way linked to the heap memory.
  • The heap memory is not as safe as the stack memory because all the threads can access the objects and we must synchronize them.
  • It is also a lot slower than the Stack memory.
  • Memory allocation and deallocation are not very simple and heap memory uses complex memory management techniques. Memory deallocation is not done automatically and Java uses a Garbage Collection mechanism to free up space.
  • Heap memory is further divided into three parts:
    • Young Generation: this part of the heap memory is the place where all newly allocated objects reside. Garbage collection takes place when this part of the memory becomes full.
    • Old Generation: when the young generation objects cross a threshold time limit, then they are transferred to the old generation space. This usually contains objects that are not frequently used.
    • Permanent Generation: this is the place where JVM stores the metadata for the runtime classes and application methods.
  • If we run out of heap memory, then the JVM throws the OutOfMemoryError. This can happen if we continue to allocate space without removing the old objects. We can simulate this error by creating an infinite loop that keeps on allocating memory for new String class objects. The following code will run for a few seconds and then we will get the OutOfMemory error
public class JavaMemoryManagement
{
	public static void outOfMemoryError()
	{
		List<String> list = new ArrayList<String>();
		while(true)
		{
			String s = "str";
			list.add(s);
		}
	}	
	public static void main(String[] args)
	{
		outOfMemoryError();
	}
}

Consider the following example to better understand the heap memory.

In the code below, we are creating a String object which will be allocated space in the String Pool of the heap memory. We are also creating an instance of the Object class and space will be allocated for this object as well. The references to these objects will be stored in the stack memory.

public class JavaMemoryManagement
{
	public static void main(String[] args)
	{
		String s = "str";
		Object o = new Object();
	}
}

The final Heap memory is shown in the image below.

Heap memory after allocation of objects

Key Differences between Stack and Heap Memory

The following table explains some of the key differences between these two types of memory.

Parameter Stack Memory Heap Memory

Usage

Stack memory is used for thread execution and storing method calls and primitive data and references associated with the method.

Heap memory is used to store the dynamically allocated objects.

Size

Stack is much smaller in size.

Heap memory is a lot larger in size than the stack.

Lifetime

Stack memory is only used during the execution of a thread or as long as the current method is running. Stack memory is short-lived.

Heap memory is used during the entire runtime of the application.

Efficiency

Stack memory is very efficient and memory access is also faster.

Heap is less efficient and much slower than the stack.

Memory Allocation and Deallocation

Memory is automatically allocated when a new method is called and deallocation also takes place automatically when a method returns.

Memory is allocated manually by the programmer when new objects are created. Memory is freed up by the garbage collector by removing objects that are no longer referenced.

Safety

Stack memory is threadsafe and each thread has its own stack which can be accessed only by the owner thread.

Heap memory is not threadsafe and the programmer must use synchronization techniques as the objects can be accessed by any thread.

Memory Management

Stack memory uses LIFO for memory management.

Memory management is quite complex in heap memory.

Error

When the stack memory is full, we get a StackOverflowError.

When the heap memory is full, we get an OutOfMemoryError.

Changing Size

The size of the stack can be altered by using the -Xss command.

We can change the initial size of the heap by using the -Xms command. We can declare the maximum heap size by using the -Xmx command.

Stack and Heap Memory Example

Let's try to understand what happens under the hood when we create new objects and call methods. Consider the following code that creates a new int variable, a String object, and another object of the Object class. It then calls the print() method that prints the string and the object.

public class JavaMemoryManagement
{
	public static void print(String s, Object o)
	{
		System.out.println(s);
        System.out.println(o);
	}	
	public static void main(String[] args)
	{
		int i = 5;
		String s = "str";
		Object o = new Object();
		print(s, o);
	}
}

The following steps explain what happens when we run the above code.

  • The execution begins with the main() method. This method will be added to the top of the stack.
  • Inside the main() method, primitive data of type int is created. This will be present in the stack itself.
  • Next, an object of the String class is created. Space will be allocated to this object in the heap memory and the reference to this object will be stored in the stack. The same thing happens when an instance of Object class is created.
  • Next, the print() method will be added to the top of the stack. The references to the String class instance and the Object class instance will be available to this method. The memory will look like below.

Memory allocation illustration

Summary

The JVM uses two types of memory for the efficient execution of our application. All primitives and method calls are stored in the stack memory and all dynamically allocated objects are stored in the heap memory. Stack is a simple type of memory that uses Last-In-First-Out order(just like the stack data structure) whereas memory management is quite complex in the heap. The heap memory is further divided into Young Generation, Old Generation, and Permanent Generation.