1. Reference Variables and Memory Allocation
- In Java, multiple reference variables can point to the same memory allocation.
- This is similar to various street signs pointing to the same house.
- Reference variables hold the memory address of an object allocated in the heap.
2. Heap Memory Structure
- Java heap is divided into:
- Young Generation:
- Eden Space (also known as Nursery Space): Where all new objects are initially allocated.
- Most objects die young and are collected quickly.
- Survivor Spaces: S0 and S1 (also called Survivor0 and Survivor1, or from-space and to-space)
- These are used to hold objects that survive one or more garbage collection cycles.
- Only one survivor space is used at a time for copying; the other remains empty.
- Eden Space (also known as Nursery Space): Where all new objects are initially allocated.
- Old (Tenured) Generation:
- Contains objects that have survived several garbage collection cycles.
- These are typically long-lived objects.
- Metaspace:
- Replaces the PermGen (Permanent Generation) space starting from Java 8.
- Used for storing class metadata, method definitions, etc.
- PermGen (Permanent Generation): Used in Java versions prior to 8 to store class metadata, method information, interned Strings, and static variables.
- Had a fixed size limit and caused
OutOfMemoryError: PermGen spaceerrors if class metadata exceeded it. - Replaced by Metaspace to allow dynamic resizing and reduce memory issues.
- Permanent Generation (PermGen) was removed and replaced with Metaspace in Java 8.
- π Exact Details
- Before Java 8: JVM used PermGen to store class metadata, interned strings, static variables, etc.
- From Java 8 onward (JDK 8, released March 2014):
- PermGen was completely removed.
- Metaspace replaced it as the memory area for class metadata.
- This change was introduced under JEP 122 β Remove the Permanent Generation.
- β Why the change?
- PermGen often caused
java.lang.OutOfMemoryError: PermGen space. - Metaspace grows dynamically (by default) based on OS memory, reducing the chance of OOM.
- Simplified memory tuning β no more
-XX:PermSize/-XX:MaxPermSize. - π New Metaspace tuning flags
-XX:MaxMetaspaceSize-XX:MetaspaceSize
- Had a fixed size limit and caused
- Young Generation:
3. Survivor Spaces and Object Movement
- Process:
- New objects are first allocated in Eden.
- When Eden fills up, a Minor GC occurs.
- Live objects from Eden and the currently used Survivor space (e.g., S0) are moved to the other empty Survivor space (e.g., S1).
- Dead objects are discarded.
- Survivor spaces “bounce”βnext GC will reverse their roles.
- After multiple cycles (controlled by
MaxTenuringThreshold), long-lived objects are promoted to the Old Generation.
Example:
String s = new String("example"); // Allocated in Eden
// After Minor GC:
// If alive -> moved to Survivor0
// If alive again in next GC -> moved to Survivor1
// Eventually -> promoted to Old Generation
4. Object Metadata Storage
- Metadata (e.g., class pointer, hash code, locking information) is stored in the object header.
- This metadata is accessed via the object pointer but stored separately in the header of the actual object.
5. Garbage Collectors and Their Algorithms
Serial Garbage Collector
- How it works: Uses a single thread for garbage collection. All application threads are paused during GC.
- GC Phases: Mark β Sweep β Compact
- Use case: Small applications with single-threaded or low-throughput requirements.
- Pros: Simplicity, low overhead.
- Cons: Long stop-the-world (STW) pauses due to single-threaded nature.
Concurrency is about dealing with many tasks at once.
Parallelism is about doing many tasks at the same time.
Concurrency can happen on a single core through context switching, while parallelism requires multiple cores.
Parallel Garbage Collector (Throughput GC)
- How it works: Uses multiple threads for Young and Old generation GC, improving throughput.
- GC Phases: Parallel Mark β Sweep β Compact (Old gen), Copying for Young gen.
- Use case: High-throughput applications like batch processing.
- Pros: Maximizes CPU usage, better throughput.
- Cons: Still experiences long STW pauses.
Concurrent Mark-Sweep (CMS) GC
- How it works: Performs most of the GC work concurrently with application threads, reducing pause times.
- GC Phases: Initial Mark (STW) β Concurrent Mark β Remark (STW) β Concurrent Sweep
- Use case: Applications needing shorter pause times.
- Pros: Reduced pause time.
- Cons: No compaction β fragmentation; higher CPU usage.
Garbage First (G1) GC
- How it works: Divides heap into equal-sized regions. Collects regions with most garbage first.
- GC Phases:
- Young GC: Copying (Eden β Survivor β Old)
- Mixed GC: Collects both young and old regions
- Full GC: STW, compacting
- Use case: Balanced throughput and low latency.
- Pros: Predictable pause times, region-based compaction.
- Cons: Slightly more complex tuning, initial learning curve.
Z Garbage Collector (ZGC)
- How it works: All major GC operations (mark, relocate, remap) are concurrent with application threads.
- GC Phases:
- Concurrent Mark β Concurrent Prepare β Concurrent Relocate β Concurrent Remap
- Use case: Large heap sizes (multi-GB to TB), ultra-low latency requirements.
- Pros: Sub-10ms pause times, handles huge heaps.
- Cons: Relatively high memory overhead, requires newer JDK.
Shenandoah GC
- How it works: Uses concurrent compaction with read/write barriers. Very low pause times.
- GC Phases: Concurrent marking β Concurrent evacuation (relocation)
- Use case: Real-time and latency-sensitive systems.
- Pros: Low latency regardless of heap size.
- Cons: High complexity, slight performance trade-offs.
When to Use Which Garbage Collector and Why New GCs Were Added
When to Use Which Garbage Collector and Why New GCs Were Added
| GC Type | Use Case | Why Added / Replaced |
|---|---|---|
| Serial GC | Small applications or client-side apps with single core CPU. | Simple, but stop-the-world pauses make it unsuitable for large or multi-threaded apps. |
| Parallel GC | Batch processing and throughput-focused apps. | Improved performance on multi-core machines, but still causes significant pause times. |
| CMS GC | Apps needing lower pause times than Parallel GC. | Reduces pause time via concurrent marking. Replaced due to fragmentation issues and complexity. |
| G1 GC | Apps needing balanced throughput and pause times. Default from Java 9+. | Replaced CMS. Offers region-based heap and predictive pause times. |
| ZGC | Applications requiring ultra-low latency and large heap sizes (multi-GB to TB scale). | Solves GC pause issues in G1 by making almost all phases concurrent. |
| Shenandoah | Low-latency apps (e.g., trading systems), where pause times must remain constant regardless of heap size. | Like ZGC but developed by RedHat with different memory model. |
6. Object Lifecycle in GC
Young Generation
- Object allocated in Eden.
- Survives Minor GC -> moved to Survivor0.
- Survives next Minor GC -> moved to Survivor1.
- After N cycles -> promoted to Old Generation.
Old Generation
- Holds long-lived objects.
- Collected during Major GC (Full GC).
- Compaction may occur depending on GC algorithm.
Finalization & Deallocation
- Objects with no references are eligible for GC.
- Finalizer methods (
finalize()) may be called. - Object is removed from memory.
7. General Steps in a Garbage Collection Cycle
Mark and Sweep (Traditional)
- Mark: Identify reachable (live) objects.
- Sweep: Reclaim memory occupied by unreachable objects.
Mark, Sweep, and Compact (e.g., Serial, Parallel GC)
- Mark: Identify live objects.
- Sweep: Reclaim dead objects.
- Compact: Move live objects together to reduce fragmentation.
Copying (e.g., Minor GC in Young Gen)
- Copy live objects from Eden + Survivor0 to Survivor1.
- Clear Eden and Survivor0.
- Swap the roles of Survivor0 and Survivor1.
8. Tuning and Configuration
-XX:+UseG1GC,-XX:+UseZGC, etc. to select GC.-Xms,-Xmxto control heap size.-XX:SurvivorRatio,-XX:MaxTenuringThreshold,-XX:NewRatioto fine-tune memory pools.
9. Default Garbage Collectors by Java Version
- Java 1.4 and earlier: Serial GC
- Java 5 & 6: Parallel GC (default)
- Java 7: Parallel GC (default), G1 GC introduced as experimental
- Java 8: Parallel GC (default), G1 stable
- Java 9 to 14: G1 GC is the default
- Java 15+: G1 GC continues as default, but ZGC and Shenandoah are available for production use
- ZGC or Shenandoah must be explicitly enabled using
-XX:+UseZGCor-XX:+UseShenandoahGC
Summary
Efficient memory management and garbage collection are key to Java performance. Understanding object lifecycle and GC algorithms helps in selecting the appropriate collector and tuning parameters for applications with different memory needs and latency requirements.