Memory Model In Java

Robin Srivastava
6 min readSep 8, 2023

--

What exactly is referred to by Memory Model?

Every computation that is performed in a computer application has its instructions and data stored in the memory (RAM to be precise).
However, to facilitate processing some constructs of a programming language stores data in different sections of the memory and treats it differently.
In this article, we will look into different components of the memory which would serve as the fundamental building blocks to design more robust applications.

Why should I invest my time to understand it?

Understanding memory model in Java would lead us to be mindful of building blocks that the “infamous” Garbage Collection takes into account before triggering its memory redemption process.
Also, it leads us to understand why we get OutOfMemory Exception and why exactly randomly increasing runtime memory is not the solution.

Apart from above, it will equip us to design better systems with less memory overhead.
Less memory overhead will lead to reduction in cloud billing as most of contemporary applications are deployed to cloud nowadays.

This acts as one of the fundamental blocks of understanding concurrency as well which I will cover in future articles.

Components of Memory Model in Java :

Hopefully, above reasons are enough to be convinced about the necessity to understand memory components and its implications.
Memory in Java is typically organized into Stack, Heap and Metaspace.
Let’s demystify each one of them.

Stack:

It’s a part of memory that is owned by the thread executing the program. Each thread will have its own Stack where in local primitive variables and reference of the object (more on this in the Heap section) are put.

Let’s say there is a method with definition as follows :

private static int calculate(int value){
int temp = value + 3;
int calculatedValue = temp * 2;
return calculatedValue;
}

Now, in Stack each of the variable defined above will be stored in LIFO manner as follows:

Hmm, okay but why bother about it?

As we have seen above, JVM creates all local primitive variables along with their values on the stack and stack is localized to the thread (i.e. each thread will have it’s own stack).
Hence, JVM can free up this memory to make space for other instructions confidently as soon as block of code or end of method is reached.
The significance of this point will become clear once we discuss heap.

Heap:

After understanding Stack and it’s significance, let’s deep dive into Heap and it’s significance.
As a thumb rule, Stack contains “local” primitive values and heap contains objects or other complex data structures storing which on Stack would lead to bloating of Stack and also would lead to unnecessary duplication of memory as objects are intended to be shared among many threads.

Unlike Stack, Heap is shared by all the threads.

I had mentioned above that stack stores the reference of the object while the actual object contents are in the heap.

Let’s solidify it with a code example:
Assume, we are defining a String literal (String literal is an immutable Object).

String str = "Robin";

Now, it will be stored in memory like this :

Thread’s stack will have reference (i.e. str) while the actual content (“Robin”) will be placed on the Heap.
Now, let’s say we do :

String ptr = str;

In this case, new reference(ptr) will be created on Stack which will also point to the same content (Robin) on the Heap as shown below.

Significance of storing objects on heap :

As we have seen above, storing objects on the heap allowed JVM to share the contents which would otherwise be unnecessary duplicated leading to reduction in memory for other processing to happen as heap is shared among other threads.

Also, as soon as the code block gets executed and object reference is removed from the stack then this object becomes eligible for Garbage Collection (more on this in the upcoming article next week).

What the heck are Escaping References :

Understanding how objects are stored between Heap and Stack would lead us to review the culprits that may lead to memory leakage via escaping reference.

Escaping references are basically object references that are “un-intentionally” passed to the calling code which may lead to unintentional modification of data or may lead to memory leakage thus preventing Garbage Collection (More on this in next week’s article).

Let’s understand it with a code example:

class EscapingReference {
private List<Integer> products = new ArrayList();

public void add(int item){
products.add(item);
}

public List<Integer> checkout(){
return products;
}
}

In above code, private reference products is being returned from the public method.
Thus, incorrectly giving access to the caller program to modify the contents of private data structure.

Do we really create all “unique” objects in Heap?

One question that pops up is that object’s contents are stored in the Heap and usually there are many use cases in our application where we can create too many Strings with the same content.

Since, String is immutable so every content is technically unique but JVM performs optimization within which it identifies the same String and instead of creating duplicate objects it points different references to the same object even though it was defined separately.
This is what is called as String pool.

Let’s say we define two Strings as follows :

String first = “Robin”;
String runnerUp = “Robin”;

Now, instead of creating two “Robin” objects in the Heap, JVM may (notice the word may as JVM decides on the fly whether String pool is needed or not) point both the references first and runnerUp to the same String object “Robin”.

What is the significance of String pool ?

Defining String literals is ubiquitous in our application and JVM provides couple of runtime arguments using which we can reduce duplicate String literals in our running application.
Thus, saving precious memory (more on this in future Java performance lectures).

Metaspace:

Metaspace as the name suggests is another fundamental block of memory model in Java ecosystem that is primarily used by the JVM to store metadata information like which code block is already converted to bytecode etc.

However, Metaspace apart from metadata also stores static data that is defined by us in our application code.

Static primitives are stored in Metaspace while static objects, though contents are stored in Heap, it’s reference is not stored in Stack but in the Metaspace.

What is the significance of Metaspace in application development?

As we have seen above, primitive variables defined on the Stack are removed by JVM after code block containing the variable is executed.
However, this doesn’t hold true in case of Metaspace.

Even if code block is executed then also data in Metaspace is not removed.
This is primarily because of the fact that Metaspace is shared among all threads and is not localized to each thread.
Thus, JVM will not be confident in removing the primitive variables even though encompassing code block is executed.

Also, above phenomenon exposes the danger that objects with reference present in Metaspace (i.e. static reference) will never be Garbage Collected (more on this in next week’s article).

P.S. :
Heap defined above is over-simplified as it consists of other fundamental components like Young Generation and Old Generation.
Young Generation is further divided into Eden space and survivor spaces but it’s relevance can be better appreciated in context with Garbage Collection.
Hence, it will be covered in next week’s article.

That’s all folks :)

Aim of this article was to set strong foundation for upcoming articles on Garbage Collection and Java performance optimizations.

I intend to demystify fundamental building blocks of Software Architecture and Development.
Do subscribe if you find my articles helpful.

--

--