Java Memory Allocation
Java memory allocation is a fundamental concept that determines where and how data is stored during program execution. Understanding memory allocation helps write efficient and bug-free code.
Memory Areas in Java
1. Stack Memory
Stack memory is used for static memory allocation and stores:
- Primitive variables
- Reference variables (addresses)
- Method call information
- Local variables
Characteristics:
- LIFO (Last In, First Out) structure
- Fast access
- Limited size (typically 1-2MB per thread)
- Automatically managed
1public void calculate() {
2 int x = 10; // Stack
3 String name = "Java"; // Reference on stack, object on heap
4 int[] numbers = new int[5]; // Reference on stack, array on heap
5
6 if (x > 5) {
7 int y = 20; // Stack (local to if block)
8 String temp = name; // Stack reference
9 } // y and temp popped from stack here
10} // x, name, numbers popped from stack here2. Heap Memory
Heap memory is used for dynamic memory allocation and stores:
- Objects
- Arrays
- Instance variables
Characteristics:
- Larger than stack
- Slower access than stack
- Managed by garbage collector
- Shared across all threads
1public class Example {
2 private int instanceVar; // Heap (part of object)
3
4 public void method() {
5 Example obj = new Example(); // Object on heap
6 int[] arr = new int[1000]; // Array on heap
7 }
8}3. Method Area
Stores:
- Class-level information
- Static variables
- Method bytecode
- Constant pool
1public class Constants {
2 public static final int MAX_SIZE = 100; // Method area
3 private static String name = "Java"; // Method area
4
5 public static void staticMethod() { // Method bytecode in method area
6 // Method implementation
7 }
8}4. Program Counter (PC Register
Stores:
- Current instruction address
- Thread-specific
Variable Allocation Examples
Primitive Variables
1public class PrimitiveAllocation {
2 public static void main(String[] args) {
3 // All primitives on stack
4 byte b = 10; // 1 byte on stack
5 short s = 20; // 2 bytes on stack
6 int i = 30; // 4 bytes on stack
7 long l = 40L; // 8 bytes on stack
8 float f = 50.5f; // 4 bytes on stack
9 double d = 60.6; // 8 bytes on stack
10 boolean bool = true; // 1 bit on stack
11 char c = 'A'; // 2 bytes on stack
12 }
13}Reference Variables
1public class ReferenceAllocation {
2 public static void main(String[] args) {
3 // References on stack, objects on heap
4 String str = "Hello"; // Reference on stack, literal in pool
5 String obj = new String("World"); // Reference on stack, object on heap
6
7 int[] numbers = new int[5]; // Reference on stack, array on heap
8 Person person = new Person(); // Reference on stack, object on heap
9 }
10}Object Memory Layout
1public class Person {
2 private String name; // Reference (4-8 bytes)
3 private int age; // Primitive (4 bytes)
4 private boolean active; // Primitive (1 byte)
5 // + Object header (typically 12-16 bytes)
6 // + Padding for alignment
7}
8
9// Memory usage:
10// Object header: ~12 bytes
11// Reference: 8 bytes (64-bit JVM)
12// int: 4 bytes
13// boolean: 1 byte
14// Padding: 3 bytes (for 8-byte alignment)
15// Total: ~28 bytes per Person objectArray Memory Allocation
One-Dimensional Arrays
1public class ArrayAllocation {
2 public static void main(String[] args) {
3 int[] numbers = new int[5];
4 // Array object on heap: 20 bytes (5 * 4 bytes)
5 // Reference on stack: 8 bytes
6
7 String[] names = new String[3];
8 // Array object on heap: 24 bytes (3 * 8 bytes for references)
9 // References initially null
10
11 names[0] = "Alice"; // String literal in string pool
12 names[1] = new String("Bob"); // String object on heap
13 names[2] = "Charlie"; // String literal in string pool
14 }
15}Multi-Dimensional Arrays
1public class MultiArrayAllocation {
2 public static void main(String[] args) {
3 int[][] matrix = new int[3][4];
4 // Outer array on heap: 24 bytes (3 * 8 bytes for references)
5 // 3 inner arrays on heap: 3 * 16 bytes (4 * 4 bytes each)
6 // Reference on stack: 8 bytes
7
8 // Ragged arrays
9 int[][] ragged = new int[3][];
10 ragged[0] = new int[2]; // 8 bytes
11 ragged[1] = new int[4]; // 16 bytes
12 ragged[2] = new int[3]; // 12 bytes
13 }
14}String Memory Allocation
String Literal Pool
1public class StringAllocation {
2 public static void main(String[] args) {
3 String s1 = "Hello"; // String pool
4 String s2 = "Hello"; // Same object as s1
5 String s3 = new String("Hello"); // Heap
6
7 System.out.println(s1 == s2); // true (same object)
8 System.out.println(s1 == s3); // false (different objects)
9 }
10}String Concatenation
1public class StringConcatenation {
2 public static void main(String[] args) {
3 // Compile-time concatenation
4 String s1 = "Hello" + " World"; // Single string in pool
5
6 // Runtime concatenation
7 String s2 = "Hello";
8 String s3 = s2 + " World"; // Creates new StringBuilder object
9
10 // Efficient concatenation in loops
11 StringBuilder sb = new StringBuilder();
12 for (int i = 0; i < 100; i++) {
13 sb.append("Item ").append(i).append("\n");
14 }
15 String result = sb.toString();
16 }
17}Garbage Collection
When Objects Become Eligible for GC
1public class GarbageCollection {
2 public static void method1() {
3 Person person = new Person("Alice");
4 person = null; // Eligible for GC
5 }
6
7 public static void method2() {
8 Person person1 = new Person("Alice");
9 Person person2 = new Person("Bob");
10 person1 = person2; // Original person1 object eligible for GC
11 }
12
13 public static void method3() {
14 createPerson(); // Person object eligible for GC after method returns
15 }
16
17 private static Person createPerson() {
18 return new Person("Alice");
19 }
20}Memory Leaks
1public class MemoryLeaks {
2 private static List<Person> cache = new ArrayList<>();
3
4 // Memory leak: Objects never removed
5 public void addToCache(Person person) {
6 cache.add(person); // Objects remain in cache forever
7 }
8
9 // Fixed: Use weak references or explicit cleanup
10 public void properCacheManagement() {
11 // Use WeakReference for automatic cleanup
12 Map<String, WeakReference<Person>> weakCache = new HashMap<>();
13
14 // Or implement explicit cleanup
15 if (cache.size() > MAX_CACHE_SIZE) {
16 cache.clear(); // Explicit cleanup
17 }
18 }
19}Memory Optimization Techniques
Object Pooling
1public class ObjectPool {
2 private static final List<Person> pool = new ArrayList<>();
3
4 public static Person getPerson() {
5 if (pool.isEmpty()) {
6 return new Person();
7 }
8 return pool.remove(pool.size() - 1);
9 }
10
11 public static void returnPerson(Person person) {
12 person.reset(); // Reset state
13 pool.add(person);
14 }
15}Primitive vs Object Choice
1public class Optimization {
2 // Good: Use primitives when possible
3 private int count; // 4 bytes
4 private double price; // 8 bytes
5
6 // Avoid: Unnecessary object creation
7 private Integer countObj; // Reference + object overhead
8
9 // Use primitive collections for large datasets
10 private IntArrayList intList; // Specialized primitive collection
11 private ArrayList<Integer> integerList; // Object overhead
12}Memory Analysis Tools
JVM Memory Flags
1# Heap size settings
2-Xms512m # Initial heap size
3-Xmx2g # Maximum heap size
4-XX:NewSize=128m # Young generation size
5-XX:MaxNewSize=512m # Maximum young generation size
6
7# Garbage collection
8-XX:+UseG1GC # Use G1 garbage collector
9-XX:+PrintGC # Print GC information
10-XX:+PrintGCDetails # Detailed GC informationMemory Profiling
1public class MemoryProfiling {
2 public static void main(String[] args) {
3 Runtime runtime = Runtime.getRuntime();
4
5 long totalMemory = runtime.totalMemory();
6 long freeMemory = runtime.freeMemory();
7 long usedMemory = totalMemory - freeMemory;
8 long maxMemory = runtime.maxMemory();
9
10 System.out.println("Used Memory: " + (usedMemory / 1024 / 1024) + " MB");
11 System.out.println("Free Memory: " + (freeMemory / 1024 / 1024) + " MB");
12 System.out.println("Total Memory: " + (totalMemory / 1024 / 1024) + " MB");
13 System.out.println("Max Memory: " + (maxMemory / 1024 / 1024) + " MB");
14 }
15}Best Practices
- Use primitives when possible to avoid object overhead
- Initialize objects properly to avoid null references
- Be aware of string concatenation in loops
- Use appropriate collections based on usage patterns
- Implement proper cleanup for resources
- Monitor memory usage in production
- Use object pooling for frequently created/destroyed objects
Common Memory Issues
- OutOfMemoryError: Heap exhausted
- StackOverflowError: Stack space exhausted
- Memory leaks: Objects not garbage collected
- Excessive object creation: Performance degradation
- Large arrays: Memory fragmentation
Understanding Java memory allocation is essential for writing efficient, scalable applications and troubleshooting memory-related issues.

