As Java developers, a good understanding of the JVM memory model helps us to plan, code and configure our applications more efficiently. It also helps us to know where to look when we need to troubleshoot issues.
This article gives a brief overview of the JVM memory model, then concentrates on two of the most important memory pools: the heap and the stack.
The JVM Memory Model
We can visualize JVM memory like this:

Fig: The JVM Memory Model
The two main subdivisions are the heap, which is managed by the JVM, and native memory, which is managed by the operating system.
The heap is further divided into:
- The Young Generation (YG): this contains newly-created objects;
- The Old Generation (OG): this contains objects that have remained in memory for some time.
This division makes garbage collection (GC) more efficient.
Most objects in memory are short-lived. For example, when a customer places an order on a website, the customer and order details will be held in memory for a few minutes only. Once the order is completed and saved to the database, they are no longer needed in RAM. They remain in the YG, and can quickly be garbage collected.
A few objects may need to be retained for the duration of the program. An example would be a database connection pool. After a time, these are promoted to the OG, which is garbage collected less frequently.
Native memory is divided into several areas:
- Metaspace: This holds metatdata related to all classes created by the program;
- Thread Space: Each running thread within the application is allocated a stack within the thread space;
- Code Cache: Holds pre-compiled code for frequently-used methods;
- Direct Buffers: These can be used to achieve more efficient I/O;
- GC: Memory reserved for the garbage collector;
- JNI: Used by the Java Native Interface;
- Misc: stores miscellaneous JVM requirements, such as symbol tables and JVM arguments.
Let’s now look in more detail at the heap and the stack.
The Heap
The heap is a central storage area, which holds all objects created, regardless of which class or which thread they belong to. Note that Java primitives, such as int, float, long etc., are not stored in the heap unless they form part of an object or an array.
Heap space is managed by the garbage collector, which periodically collects and removes any objects that no longer have valid references pointing to them. Objects are initially created in the YG, which is usually quite small. If they survive a few cycles of garbage collection, they are promoted to the OG, which is much larger.
The following errors may occur if the heap runs out of memory:
- java.lang.OutOfMemoryError: Java Heap Space
- java.lang.OutOfMemoryError: GC
To set the size of the heap, we can use the following command line arguments when the JVM is invoked:
- -Xms sets the initial heap size;
- -Xmx sets the maximum heap size.
The Stack
Stacks are held within the thread space. Each thread has its own stack.
Each stack holds the thread’s program counter (a pointer to the next instruction to be executed) and a series of stack frames. As a method is invoked by the thread, a frame is created for it, and pushed onto the stack. When the method completes, it is popped from the stack, so that stack memory is automatically removed as soon as it’s no longer needed.
Each stack frame contains:
- Local variables. If they are primitives, they are held directly in the stack frame. If they are objects, they are stored in the heap, but the stack frame stores a reference pointer to the actual object.
- The operand stack. This holds intermediate results and partial computations.
- The method’s return address
- A pointer to the method’s area in the constant pool
- Pointers to exception handlers.
We can visualize stack space like this:

Fig: JVM Stack Space
Note that local variables defined within methods will be stored in the stack frame if they are Java primitives. However, if they are objects, they are stored in the heap, and the stack frame stores a pointer to their position in the heap. Once a method completes, the pointers to any objects it created will no longer exist, and the object will be garbage collected unless it is referred to elsewhere.
If a stack grows too large, the application will throw an error: StackOverflowError. For more information on stacks and dealing with stack overflow errors, see this article: StackOverflowError Causes and Solutions.
We can set the maximum size that any stack can occupy using the command line argument -Xss.
How Do the Stack, the Heap and the Garbage Collector Work Together?
Each time a method is called by a thread, a stack frame is created for it. Local variables are created within the frame if they are primitives. If they are objects, they’re created in the heap, with pointers held within the stack frame.
When a method completes, the frame, including the pointers to objects in the heap, is removed from the stack.
One of the first tasks of the garbage collector is to scan through the stack, looking for references to objects in the heap. These references are known as garbage roots. The GC marks these objects, and all the objects which they in turn may refer to, as being in use. This then prevents them from being garbage collected.
For a demonstration of what’s held in the heap and the stack during the execution of a sample program, watch this video: Java Program Execution.
Conclusion
The heap is a central storage area for all objects created by an application. The stack holds information relating to each running thread, with a stack frame for each active method.
For more information, you may like to read this article: Java Heap, Stack and GC: What You Need To Know.
Understanding the JVM memory model helps us to write better programs, and more easily troubleshoot them when they go wrong.

Share your Thoughts!