Introduction
Understanding Java's memory model is crucial for writing efficient applications and debugging memory-related issues. In this guide, we'll explore how Java manages memory, the role of heap and stack, and how garbage collection works.
Java Memory Architecture
Java memory is divided into several regions:
1. Heap Memory
The heap is where all objects are stored. It's shared among all threads.
public class HeapExample {
public static void main(String[] args) {
// Objects are stored in heap
String name = new String("JavaCompiler"); // Heap allocation
int[] numbers = new int[1000]; // Array in heap
// Object reference (name) is in stack, object data in heap
}
}
Heap Regions:
- Young Generation: New objects are allocated here
- Old Generation (Tenured): Long-lived objects
- Metaspace: Class metadata (replaces PermGen in Java 8+)
2. Stack Memory
Each thread has its own stack containing:
- Method call frames
- Local variables (primitives and references)
- Return addresses
public class StackExample {
public static void main(String[] args) {
int x = 10; // Stored in stack
int result = multiply(x, 5); // New stack frame created
System.out.println(result);
}
static int multiply(int a, int b) {
int product = a * b; // All local variables in stack
return product;
} // Stack frame popped after return
}
Stack vs Heap
| Aspect | Stack | Heap |
| Speed | Fast | Slower |
| Size | Limited | Large |
| Thread Safety | Thread-safe | Requires synchronization |
| Memory Management | Automatic (LIFO) | Garbage Collected |
| Stores | Primitives, references | Objects |
Garbage Collection
Java automatically reclaims memory from unused objects through garbage collection.
How GC Works
GC Roots
- Static variables
- Local variables in active threads
- Active threads themselves
- JNI references
public class GCExample {
static Object staticRef; // GC root
public static void main(String[] args) {
Object localRef = new Object(); // Reachable from stack
staticRef = new Object(); // Reachable from static
Object temp = new Object();
temp = null; // Now eligible for GC
// Suggest GC (not guaranteed)
System.gc();
}
}
Types of Garbage Collectors
Memory Monitoring
JVM Memory Options
# Set initial heap size
java -Xms512m MyAppSet maximum heap size
java -Xmx2g MyAppSet stack size
java -Xss512k MyAppEnable GC logging
java -Xlog:gc* MyApp
Monitoring Code
public class MemoryMonitor {
public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();
long maxMemory = runtime.maxMemory();
long totalMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
long usedMemory = totalMemory - freeMemory;
System.out.println("Max Memory: " + (maxMemory / (1024 * 1024)) + " MB");
System.out.println("Total Memory: " + (totalMemory / (1024 * 1024)) + " MB");
System.out.println("Free Memory: " + (freeMemory / (1024 * 1024)) + " MB");
System.out.println("Used Memory: " + (usedMemory / (1024 * 1024)) + " MB");
}
}
Common Memory Issues
1. Memory Leak
// Memory leak example - don't do this!
public class MemoryLeak {
private static List<Object> cache = new ArrayList<>();
public void addToCache(Object obj) {
cache.add(obj); // Objects never removed, memory grows forever
}
}
2. OutOfMemoryError
// This will cause OutOfMemoryError
public class OOMExample {
public static void main(String[] args) {
List<byte[]> list = new ArrayList<>();
while (true) {
list.add(new byte[1024 * 1024]); // 1MB each
}
}
}
Best Practices
Summary
Understanding Java memory management helps you:
- Write more efficient code
- Debug memory issues
- Tune JVM parameters for optimal performance