Sitemap
Mario Bui

Mario Bui

4+ Exp Software Engineer

Core Java: Memory Management

5 min readJan 10, 2026

--

This overview will go through how Java manage the memory, its key components, and how to optimize memory usage effectively

1. Introduction

Memory Management is a crucial aspect for any Java application. Java offers an automatic memory management tool through Gabage Collection (GC). Hence, Java relieves developer from the need for manual memory allocation and deallocation like C/C++.

However, the way you handle or structure your code still matters and can affect to the memory and GC cannot not help you if your app crashes :)

2. Java Memory Structure

Java devides memory into differents regions to manage the execution of programs. It consists of:

Press enter or click to view image in full size
Java Memory Model
  1. Heap Memory (IMPORTANT) — Store objects and class instances
  2. Stack Memory (IMPORTANT) — Store local variables, local references and function calls
  3. Metaspace (Method Area) — Store class metadata, method code, and runtime constant pool
  4. PC Register — Keep track of the current instruction being executed
  5. Native Method Stack — Handle native method executions (Non-Java code like C/C++)

3. Heap Memory

Heap memory is the largest memory area in Java and is used to store all objects at runtime. It is divided into Young Generation and Old Generation:

3.1 Young Generation

  • Eden: The newly objects are created for the first time (No GC scan yet)
  • Survivor Spaces: When Eden is fill up, a Minor GC (mentioned below) occurs and live objects are moved to a Survivor space

3.2 Old Generation (Tenured Space): Objects that survived mutiple Minor GCs are promoted to Old Generation

=> When Heap is full, it throw OutOfMemory error

class Person {
String name;
Person(String name) {
this.name = name;
}
}

public class MemoryExample {
public static void main(String[] args) {
Person p = new Person("Alisa"); // Stored in Heap Memory
}
}

4. Stack Memory

Stack memory is a fixed-size memory regions that stores method calls and local variables.

public class StackExample {
public static void main(String[] args) {
int x = 100; // Stored in Stack Memory
String message = "Hello Java"; // Reference stored in Stack, object stored in Heap
displayMessage(message);
}

static void displayMessage(String msg) {
System.out.println(msg);
}
}

=> When Stack is full, it throw StackOverFlow error

5. Stack vs Heap Visualization

❓Question: For each numbered comment, decide STACK or HEAP.

public class HeapStackTest {

public static void main(String[] args) {
int x = 10; // (1)
Person p = new Person(); // (2)
p.age = 30; // (3)
test(p); // (4)
}

static void test(Person p2) { // (5)
int y = 20; // (6)
Person p3 = p2; // (7)
}
}

class Person {
int age; // (8)
}

(1) => stack (primitive local variable)

STACK (main)               HEAP
------------------ ------------------
x = 10

(2) => p in Stack , new object Person created in Heap

STACK (main)               HEAP
------------------ ------------------
x = 10
p ───────────────▶ Person@A {
age = 0
}

(3) => data in Heap change

STACK (main)               HEAP
------------------ ------------------
x = 10
p ───────────────▶ Person@A {
age = 30
}

(4) => Stack (method call)

STACK (main)               HEAP
------------------ ------------------
x = 10
p ───────────────▶ Person@A {
age = 30
}

STACK (test) HEAP
------------------ ------------------
p2 ──────────────▶ Person@A {
age = 30
}

(5) (6)=> p2 in stack

STACK (test)               HEAP
------------------ ------------------
p2 ──────────────▶ Person@A {
age = 30
}
p3 ──────────────▶
y = 20

(7) p2, p3 in stack, new Person object is created in Heap

STACK (test)               HEAP
------------------ ------------------
p2 ──────────────▶ Person@A {
age = 30
}
p3 ──────────────▶
y = 20

(8) in Heap (age belongs to instance class)

STACK (main)               HEAP
------------------ ------------------
x = 10
p ───────────────▶ Person@A {
age = 30
}

6. Gabarge Colletion in Java

Garbage Collection in Java is the process of reclaiming memory by deleting objects that are no longer referenced.

Get Mario Bui’s stories in your inbox

Join Medium for free to get updates from this writer.

Core Steps of Garbage Collection:

  1. Marking: The process of identifying all the active, reachable objects.
  2. Sweeping: Unmarked (unused) objects are removed, freeing up space.
  3. Compacting: To avoid memory fragmentation, the remaining objects are often compacted to leave contiguous free space.

How GC internaly works ?

  • Minor GC (MOST FREQUENT): Triggered when Eden is full, so it’s fast
  • Major GC: Triggered when Old Generation Space is full, so it’s fast
  • Full GC: Triggered when Minor GC + Major GC can’t free enough memory

=> Each type of GC runs directly affects on the application performance and latency (JVM manages to make sure all of this Consistent)

7. Different Gabarge Colletion Algorithms

  • Serial GC — Best for small applications with a single-threaded environment.
  • Parallel GC — Uses multiple threads for faster garbage collection.
  • CMS (Concurrent Mark-Sweep) GC — Run concurently with the application in multiple phases => Legagy, make higher CPU load
  • G1 GC (Garbage First) — Devides Heap into regions and prioritize the region that has the largest garbage, helps balance performance and memory efficiency, used in large applications. => Default since Java 9
  • ZGC & Shenandoah GC — Low-latency garbage collectors for real-time applications. (Java 11)

8. Reference kinds (GC / memory management level)

In Java, a reference is a variable.

Person p; // => p → reference variable
  • Strong Reference (DEFAULT) — 99% of references you use
Person p = new Person();
  • Soft Reference — Cleared only when memory is low
SoftReference<Person> ref =
new SoftReference<>(new Person());
  • Weak Reference — Cleared on next GC, Used in WeakHashMap
WeakReference<Person> ref =
new WeakReference<>(new Person());
  • Phantom Reference — Used for cleanup after GC
PhantomReference<Person> ref =
new PhantomReference<>(new Person(), queue);

9. Best Practices for Memory Optimization

Memory leaks is when an unused objects still exist in the application and survived from the GC, because it still have references => The more time application runs, The more memory increases

Example:

import java.util.ArrayList;
import java.util.List;

public class MemoryLeakDemo {

// GC root → objects added here will never be freed
static List<byte[]> cache = new ArrayList<>();

public static void main(String[] args) {
while (true) {
cache.add(new byte[1_000_000]); // 1 MB
// object is no longer needed, but still referenced
}
}
}

Best Pratices

1. Detect Memory Leaks: Use tools like VisualVM or YourKit to identify memory leaks and unnecessary object retention.

2. Use Efficient Data Structures: Selecting the right data structures can significantly reduce memory usage.

  • Use primitive data types instead of objects where possible (int vs Integer).
  • Opt for memory-efficient options like StringBuilder and StringBuffer where applicable.

3. Close database connections, file streams, and network sockets properly.

try {
BufferedReader br = new BufferedReader(new FileReader(inputFile));
...
...
} catch (Exception e) {
e.printStackTrace();
br.close() // Native methods in Native Method Stack
// GC only handle and track in Heap Memory
}

10. Conclusion

Memory management in Java is handled by the JVM using automatic garbage collection, which makes development easier and reduces memory issues. By understanding heap and stack memory, tuning GC when needed, and following good practices, developers can build efficient and reliable applications.

--

--