13 Jun JVM Performance Tuning Part 2: Garbage Collection Theory
Garbage collection is often the most misunderstood feature of the Java Virtual Machine. It’s often advertised as moving the responsibility of memory management from the application developer to the Java Virtual Machine. This just isn’t the case. On the other hand, the developer doesn’t need to put too much effort in pleasing the garbage collector.
A good understanding of garbage collection theory is necessary for writing high performant applications for the Java platform. Part 2 of the JVM performance tuning blog entries will discuss the theoretical process of garbage collection in detail.
An important question we could ask ourselfs is why we should care about garbage collection?
There is a cost related to the allocation and collection of memory. It can play an important role in how the software performs especially when the application requires large amounts of RAM and forces the OS to use virtual memory. This behaviour often occurs when programs have memory leaks, meaning that memory will be allocated, but not properly released. Although the JVM is responsible for freeing unused memory, a developer has to make it clear what is being unused.
Garbage collection is the process of cleaning up unreachable java objects. Object are said to be unreachable when no more strong reference to it exist. As soon as an object is unreachable it can be collected by the GC. The object is a candidate for collection, but this doesn’t mean it will be immediately collected.
The global working of the garbage collection contains three phases:
- Lock it down: Objects participating in garbage collection first need to be locked.
- Mark: iteration phase. Al unreachable object will be marked.
- Sweep: sweeping phase of al the marked objects.
No mather what type of garbage collector is being used, these three phases can be found in any of them. The most import characteristics of different types of garbage collectors are the different kind of strategies being used:
- Serial versus Parallel: Serial GC uses only one thread to perform garbage collection, even when multiple CPU’s are available. Parallel GC uses multiple threads to execute GC in parallel. This introduces a little more overhead, but the use of multiple thread decreases the total GC overhead.
- Concurrent versus Stop the World: A stop the world GC stops all the current running application threads at the moment the garbage collector will start cleaning up the dead objects. At that moment the application seems to be frozen. With a concurrent GC only a small part of the overal GC process will stop the application threads. The largest part of GC occurs concurrently with the application threads. Since the state of an object can change during concurrent GC, this type of GC introduces extra overhead an memory requirements.
- Compacting versus non-compacting versus copying: At the moment the GC has deleted the unreachable objects, the GC can decide to perform a compacting operation. It means all the remaining objects will be put next to each other in the java heap to avoid fragmentation. Compacting introduced extra overhead during GC, but makes allocation of new objects faster, since the JVM doesn’t need to search for a place in the fragmented heap to store the object. A copying collector copies the objects to another place in memory.
- CMS: concurrent mark sweep (explained in detail later)
GC performance can be measured based on performance metrics:
- Throughput: % of the time not spent on GC
- Garbage Collection Overhead: % of the time spent on GC
- Pause Time: the time application threads will be stopped to perform GC
- Frequency of Collection: how often GC occurs
- Footprint: how many resources (memory and CPU) the GC needs
- Promptness: the time between an object become garbage and the time the object will be cleaned