In this article, we demonstrate various methods to simulate a java.lang.OutOfMemoryError. We use practical examples so that we can better understand how our applications might run out of memory when objects are continuously allocated. These examples help us to test our error handling and tuning of Java heap space in real-world scenarios.
Table of contents
- Example 1: Using an ArrayList to Consume Heap Memory
- Example 2: Simulating Memory Leak with a HashMap
- Example 3: Continuous String Allocation
- Real-World Implications and Use Cases
- References
Example 1: Using an ArrayList to Consume Heap Memory
In our first example, we simulate an OutOfMemoryError by creating 1MB objects (byte arrays) in an infinite loop and adding them to an ArrayList. Each loop iteration allocates a new byte array of 1MB, and since we never remove these objects, the JVM eventually runs out of heap memory.
package com.mkyong;
import java.util.ArrayList;
import java.util.List;
public class JavaEatMemory {
public static void main(String[] args) {
List<byte[]> list = new ArrayList<>();
int index = 1;
while (true) {
// Allocate 1MB each loop (1 x 1024 x 1024 = 1048576 bytes)
byte[] b = new byte[1048576];
list.add(b);
// Get the current free memory
Runtime rt = Runtime.getRuntime();
System.out.printf("[%d] free memory: %s%n", index++, rt.freeMemory());
}
}
}
Output:
[14299] free memory: 2134669728
[14300] free memory: 2133621136
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.mkyong.wpbackend.TestJava.main(JavaEatMemory.java:13)
In this example, we actively monitor the free memory and observe how it decreases until the JVM throws an OutOfMemoryError.
Example 2: Simulating Memory Leak with a HashMap
In this second example, we simulate a memory leak using a HashMap. Here, we continuously add entries to the map without ever removing any of them. This approach is similar to the first example but uses a different data structure, which can be useful when we want to see how different collections impact memory consumption.
package com.mkyong;
import java.util.HashMap;
import java.util.Map;
public class MemoryLeakSimulator {
public static void main(String[] args) {
Map<Integer, byte[]> map = new HashMap<>();
int index = 1;
while (true) {
// Allocate 1MB and put it into the HashMap
byte[] b = new byte[1048576];
map.put(index, b);
// Display current free memory
Runtime rt = Runtime.getRuntime();
System.out.printf("Entry %d added, free memory: %s%n", index++, rt.freeMemory());
}
}
}
Output:
Entry 14295 added, free memory: 2138392136
Entry 14296 added, free memory: 2137343544
Entry 14297 added, free memory: 2136294952
Entry 14298 added, free memory: 2135246360
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.mkyong.wpbackend.TestJava.main(MemoryLeakSimulator.java:13)
In this example, we simulate how a memory leak occurs when a map continuously grows. We actively add new entries and monitor memory usage, just like in the ArrayList example.
Example 3: Continuous String Allocation
Another way to simulate an OutOfMemoryError is by concatenating strings in a loop. Although not as direct as allocating large arrays, string concatenation can eventually fill up the heap if we keep the growing string in memory.
package com.mkyong;
public class StringMemoryLeak {
public static void main(String[] args) {
String data = "";
int index = 1;
while (true) {
// Append a constant string to the existing data
data += "memoryLeak";
if (index % 1000 == 0) {
// Print memory information every 1000 iterations
Runtime rt = Runtime.getRuntime();
System.out.printf("After %d iterations, free memory: %s%n", index, rt.freeMemory());
}
index++;
}
}
}
In this example, we keep concatenating strings. The growing string will eventually consume a large portion of the heap, leading the JVM to throw an OutOfMemoryError.
Real-World Implications and Use Cases
When we simulate these scenarios, we can better understand how memory leaks occur and how our applications might behave under heavy memory load. These examples serve as a reminder that:
- Memory Management: We must always manage our collections and object references wisely.
- Error Handling: We should implement proper error handling for OutOfMemoryError in critical systems.
- Performance Tuning: Monitoring and tuning the Java heap is essential for robust application performance.
By testing our applications using these simulation techniques, we can improve performance and ensure our software is robust enough to handle memory-intensive operations.