JVM Memory Allocation and Garbage Collection Strategies
Java’s automatic memory management relies on well-defined allocation and reclamation strategies within the heap. These mechanisms ensure efficient object lifecycle handling without manual intervention.
1. New Objects Allocated in Eden
By default, newly created objects are placed in the Eden space of the young generation. When Eden lacks sufficient contiguous space, a Minor GC is triggered to reclaim unused objects.
public class EdenAllocation {
private static final int MB = 1024 * 1024;
public static void allocate() {
byte[] obj1 = new byte[2 * MB];
byte[] obj2 = new byte[2 * MB];
byte[] obj3 = new byte[2 * MB];
byte[] obj4 = new byte[4 * MB]; // Triggers Minor GC if Eden is full
}
public static void main(String[] args) {
allocate();
}
}
VM arguments: -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -verbose:gc
2. Large Objects Bypass Young Generation
Objects exceeding a certain size threshold are allocated directly in the old generation to avoid expensive copying between Eden and Survivor spaces during Minor GCs. This behavior is controlled by -XX:PretenureSizeThreshold.
public class LargeObjectAllocation {
private static final int MB = 1024 * 1024;
public static void allocateLarge() {
byte[] bigObj = new byte[4 * MB]; // Goes straight to old gen if > threshold
}
public static void main(String[] args) {
allocateLarge();
}
}
VM arguments include: -XX:PretenureSizeThreshold=3145728 (3MB)
3. Aging and Promotion to Old Generation
Each time an object survives a Minor GC, its age counter increments. Once it reaches the maximum tenuring threshold (default: 15), it is promoted to the old generation. The threshold can be adjusted via -XX:MaxTenuringThreshold.
public class ObjectAging {
private static final int MB = 1024 * 1024;
public static void testPromotion() {
byte[] small = new byte[MB / 4];
byte[] large1 = new byte[4 * MB];
byte[] large2 = new byte[4 * MB];
large2 = null;
large2 = new byte[4 * MB]; // May trigger promotion based on age
}
public static void main(String[] args) {
testPromotion();
}
}
Use -XX:+PrintTenuringDistribution to observe age distribution.
4. Dynamic Age-Based Promotion
HotSpot may promote objects earlier than the configured MaxTenuringThreshold if the total size of objectss of a given age exceeds half the Survivor space. This optimization reduces Survivor pressure.
public class DynamicPromotion {
private static final int MB = 1024 * 1024;
public static void testDynamicAge() {
byte[] a = new byte[MB / 4];
byte[] b = new byte[MB / 4]; // Combined, they exceed half of Survivor
byte[] c = new byte[4 * MB];
byte[] d = new byte[4 * MB];
d = null;
d = new byte[4 * MB];
}
public static void main(String[] args) {
testDynamicAge();
}
}
Run with -XX:MaxTenuringThreshold=15 -XX:+PrintTenuringDistribution.
5. Space Allocation Guarantee
Before a Minor GC, the JVM checks whether the old generation has enough free space to accommoadte all live objects from the young generation. If not, it evaluates historical promotion data:
- If
-XX:-HandlePromotionFailureis set (default in newer JDKs), a Full GC is triggered when old gen space is insufficient. - In older versions, if
HandlePromotionFailurewas enabled, the JVM compared available space against average promotion size before deciding.
Since JDK 6u24, the rule simplifies: Minor GC proceeds only if old gen has more free space than the total young gen size; otherwise, Full GC occurs.
public class SpaceGuarantee {
private static final int MB = 1024 * 1024;
public static void testGuarantee() {
byte[] o1 = new byte[2 * MB];
byte[] o2 = new byte[2 * MB];
byte[] o3 = new byte[2 * MB];
o1 = null;
byte[] o4 = new byte[2 * MB];
byte[] o5 = new byte[2 * MB];
byte[] o6 = new byte[2 * MB];
o4 = o5 = o6 = null;
byte[] o7 = new byte[2 * MB]; // May trigger Full GC due to space guarantee
}
public static void main(String[] args) {
testGuarantee();
}
}
VM argument: -XX:-HandlePromotionFailure forces conservative behavior.