This series is a summary of Oracle's JVM troubleshooting course which gives an overview on JVM memory management, Hotspot VM's garbage collection options, various memory errors and how to troubleshoot them.
In this post (part 1), we will have a look at how JVM manages memory and its different garbage collectors.
You can find Part 2 here:
The JVM provides automatic memory management to free the programmer from manually managing memory. New objects are allocated on heap memory. A root set consists of pointers to external memory, static variables, threads, JNI references and internal JVM structures. Objects directly reachable from the root set must be kept in heap. Objects reachable from any of the reachable objects must also be in heap. This group of objects are the only ones which can be used by a program. The unreachable objects (garbage) are removed using a process called garbage collection (GC). Reachable objects are compacted i.e moved to contiguous space in heap. This is important as otherwise, the heap will become fragmented.
When the JVM starts, it requests some memory from the OS. This memory is divided into various spaces.
- Separate pools hold objects of diff age ranges
- JVM's GC is generational and is based on the hypothesis that:
- Most objects die young
- Few references from older to younger objects
- There are 2 generations of objects:
- young : small and collected frequently (minor collection). Objects which survive a threshold number of GCs move to old generation.
- old : large, collected infrequently (major collection = Full GC)
- Prior to JDK 8, there was also a permanent generation which was for storing class representations and metadata, interned strings and class statics. This was replaced by meta-space in JDK 8 and later.
Meta-space is allocated in native memory. It is managed through JVM options MetaspaceSize for initial size and MaxMetaspaceSize for maximum.
- If UseCompressedClassPointers is enabled, 2 areas of memory are used for storing classes and their metadata - meta-space and compressed class space. 64 bit class pointers represented with 32 bit offsets to save space. Class metadata is referenced by 32 bit offsets stored in compressed class space. By default, compressed class space is 1 GB.
- Code cache is used to store compiled code generated by JIT (Just in time optimizer), allocated out of native memory and managed by Code Cache Sweeper
The JVM has different garbage collection methods for different generations of objects. Some of them are described below:
- Young generation collection
- Serial - Stop-the-world (STW), copying collector, single threaded
- ParNew - STW, copying collector, multiple GC threads
- Parallel Scavenge - STW, copying collector, multiple GC threads
- Old generation collection
- Serial Old - STW, mark-sweep-compact collector, single threaded
- CMS - Mostly concurrent, low pause
- Parallel Old - compacting collector, multiple GC threads
- G1 : designed for large heaps and offers predictable short pauses.
- Has different memory layout for generations
- Same collector for all generations
These are the option flags passed to JVM for specifying which GC to use:
- UseSerialGC : Serial + SerialOld
- UseParNewGC : ParNew + SerialOld . In JDK 9, uses CMS for old gen
- UseConcMarkSweepGC : ParNew + CMS + SerialOld
- CMS used most of time to collect old generation. SerialOld used when concurrent mode failure occurs.
- CMS performs most work in concurrent with application threads.
- No heap compaction leads to fragmentation. Has floating garbage and requires larger heap sizes.
- Free space maintained as linked lists. Allocation expensive compared to bump-the-pointer allocations.
- Additional overhead on young collections
- Deprecated in JDK 9
- UseParallelGC : Parallel Scavenge + Parallel Old.
- Maximizes throughput.
- Default GC till JDK 9
- UseG1GC - G1 for both generations
- Server style GC for multi-core machines with large memory
- Low GC pauses with high probability while trying for high throughput
- Compacting collector. Low pauses without fragmentation
- Better GC ergonomics. Parallel threads and some tasks are concurrent with application threads
- Available since JDK 7u4 and default in JDK 9
For more detailed information on tuning the garbage collector, read the official GC Tuning Guide
- When Eden space in young gen is full, reachable objects are marked and moved to the ToSurvivorSpace
- Objects in FromSurvivor space that are reachable are moved to ToSurvivorSpace
- Objects in FromSurvivor space that have crossed the threshold are promoted to the old generation
- Eden becomes empty and is ready for new allocations
- To and From Survivor Spaces are switched
- Mark phase : marks all live objects
- Sweep phase : sweeps over heap identifying garbage
- Slide phase : GC performs a sliding compaction by sliding live objects to the start of the heap
One of the most consolidated misconceptions about programming, since the early days, is the idea that such activity is purely technical, completely exact in nature, like Math and Physics. Computation is exact, but programming is not.