Java tutorials > Java Virtual Machine (JVM) > Memory Management and Garbage Collection > Different parts of JVM memory (Heap, Stack, Metaspace/PermGen)?

Different parts of JVM memory (Heap, Stack, Metaspace/PermGen)?

The Java Virtual Machine (JVM) manages memory automatically, freeing developers from manual memory allocation and deallocation. Understanding the different parts of JVM memory is crucial for optimizing performance and troubleshooting memory-related issues. The main areas are the Heap, Stack, and Metaspace (formerly PermGen).

JVM Memory Areas Overview

The JVM divides memory into several runtime data areas. These areas hold data and information during program execution. The key areas we'll focus on are:

  • Heap: Used for dynamic memory allocation for objects. All class instances and arrays are stored in the heap.
  • Stack: Each thread has its own JVM stack, used for storing local variables, method call information, and partial results.
  • Metaspace (formerly PermGen): Stores class metadata, such as class definitions, method information, and constant pool data. In Java 8, PermGen was replaced with Metaspace, which is allocated from native memory.

Heap Memory

The Heap is the runtime data area from which memory for all class instances and arrays is allocated. It is shared by all threads. The heap is created when the JVM starts and may be of a fixed or variable size. The heap is where garbage collection occurs. It's typically divided into:

  • Young Generation: Where newly created objects are allocated. It's further subdivided into Eden Space and Survivor Spaces (S0 and S1). Minor GC (Garbage Collection) occurs frequently in the Young Generation.
  • Old Generation: Objects that have survived several minor GC cycles are moved to the Old Generation. Major GC occurs less frequently in the Old Generation.
  • Permanent Generation (PermGen) / Metaspace: (Pre Java 8 / Java 8 and later) Stores class metadata and interned strings. Replaced by Metaspace in Java 8. Metaspace allows for dynamic allocation of memory from native memory, reducing the risk of `java.lang.OutOfMemoryError: PermGen space`.

Heap Example - Object Allocation

This code snippet demonstrates that when we create objects using the new keyword, the memory for these objects is allocated on the heap. Both obj1 and obj2 will reside in the heap. Garbage Collection will eventually reclaim this memory if these objects become unreachable.

public class HeapExample {
    public static void main(String[] args) {
        Object obj1 = new Object(); // Object allocated on the heap
        Object obj2 = new Object(); // Another object allocated on the heap
    }
}

Stack Memory

The JVM stack is a per-thread runtime data area. Each time a new thread is launched, the JVM creates a new stack for that thread. The stack stores frames, which hold data related to method invocations, including local variables, parameters, and return addresses. Stacks are LIFO (Last-In, First-Out) data structures. When a method is called, a new frame is pushed onto the stack; when the method returns, the frame is popped off the stack. Stack memory is automatically managed by the JVM.

Key characteristics of Stack Memory:

  • Thread-specific
  • Stores local variables and method call information
  • Automatically managed (allocation and deallocation)
  • Limited in size (stack overflow errors can occur)

Stack Example - Method Invocation

In this example, when the main method calls the add method, a new frame is pushed onto the stack for the add method. This frame contains the local variables a, b, and sum. When the add method returns, its frame is popped off the stack, and the result is passed back to the main method's frame.

public class StackExample {
    public static void main(String[] args) {
        int result = add(5, 3);
        System.out.println("Result: " + result);
    }

    public static int add(int a, int b) {
        int sum = a + b;
        return sum;
    }
}

Metaspace (formerly PermGen)

Metaspace, introduced in Java 8, replaced the Permanent Generation (PermGen). It stores metadata about classes, such as class definitions, method information, and constant pool data. The key difference is that Metaspace is allocated from native memory, whereas PermGen was part of the heap.

Benefits of Metaspace:

  • Dynamic resizing: Metaspace can grow dynamically, reducing the risk of `java.lang.OutOfMemoryError: PermGen space`.
  • Native memory allocation: Uses native memory instead of the heap, allowing more efficient memory management.

Metaspace Example - Class Loading

When a class is loaded by the classloader (e.g., using Class.forName), its metadata is stored in Metaspace. This metadata includes the class's name, fields, methods, and constant pool information. The larger the number of classes loaded, the more memory is consumed in the Metaspace.

public class MetaspaceExample {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> myClass = Class.forName("com.example.MyClass"); // Loads the class
        System.out.println("Class loaded: " + myClass.getName());
    }
}

// Assume com.example.MyClass is a simple class
package com.example;

public class MyClass {
    // Class definition
}

Real-Life Use Case Section

Web Applications: Web applications often load and unload classes dynamically as new features are deployed or code is updated. This can lead to significant memory usage in the Metaspace. Properly configuring the Metaspace size is critical to avoid `OutOfMemoryError` exceptions.

Large Data Processing: Applications that process large datasets often create many objects, placing heavy demands on the Heap. Tuning the Heap size and garbage collection parameters is essential for optimal performance.

Multi-threaded Applications: Applications with many threads require careful management of Stack memory. If the stack size is too small, `StackOverflowError` exceptions can occur.

Best Practices

Heap:

  • Monitor heap usage regularly using tools like JConsole, VisualVM, or JProfiler.
  • Tune garbage collection parameters (e.g., -Xms, -Xmx, -XX:NewRatio) based on application requirements.
  • Avoid creating unnecessary objects to reduce the load on the garbage collector.

Stack:

  • Avoid deeply nested method calls to prevent stack overflow errors.
  • Increase the stack size using the -Xss option if necessary (but be aware of the memory overhead).

Metaspace:

  • Monitor Metaspace usage using tools like JConsole, VisualVM, or JProfiler.
  • Set the maximum Metaspace size using the -XX:MaxMetaspaceSize option to prevent excessive memory consumption.

Interview Tip

When discussing JVM memory management in interviews, demonstrate a clear understanding of the Heap, Stack, and Metaspace/PermGen. Be prepared to discuss the role of garbage collection, the differences between PermGen and Metaspace, and common memory-related issues like `OutOfMemoryError` and `StackOverflowError`. Provide real-world examples where you've tuned JVM memory settings for performance optimization.

When to use them

Heap: Whenever your application needs to create objects dynamically. Most Java applications heavily rely on the heap.

Stack: Every method call utilizes stack memory to store local variables and execution context.

Metaspace: Every time a class is loaded, metadata is stored in Metaspace.

Memory footprint

The memory footprint of each area depends on the application's behavior and configuration:

  • Heap: Can be significant for applications with many objects and large datasets.
  • Stack: Typically smaller, but can become a concern for heavily multi-threaded applications with large stack sizes.
  • Metaspace: Depends on the number of classes loaded by the application.

Alternatives

Alternatives to managing memory manually (which is generally discouraged in Java) include:

  • Object Pooling: Reusing existing objects instead of creating new ones, reducing the load on the garbage collector.
  • Using Data Structures Wisely: Selecting efficient data structures (e.g., using `StringBuilder` instead of repeatedly concatenating strings) to minimize memory usage.
  • Off-Heap Storage: Storing large data outside the heap, using libraries like Chronicle Map or Ehcache, to avoid garbage collection overhead.

Pros

Heap: Automatic garbage collection simplifies memory management.

Stack: Fast allocation and deallocation of memory for method calls.

Metaspace: Dynamic resizing and native memory allocation improve stability compared to PermGen.

Cons

Heap: Garbage collection can introduce pauses in application execution.

Stack: Limited in size, leading to potential stack overflow errors.

Metaspace: Requires careful monitoring to avoid excessive memory consumption.

FAQ

  • What is the difference between PermGen and Metaspace?

    PermGen was part of the heap and had a fixed size, which could lead to `OutOfMemoryError` exceptions. Metaspace is allocated from native memory and can grow dynamically, reducing the risk of `OutOfMemoryError`.
  • What is a StackOverflowError?

    A `StackOverflowError` occurs when the call stack exceeds its maximum size, typically due to deeply nested method calls or infinite recursion.
  • What is OutOfMemoryError?

    An `OutOfMemoryError` occurs when the JVM cannot allocate memory for a new object because the heap is full or Metaspace has reached its maximum size.