Wednesday, July 22, 2015

Garbage Collector : An overview from JVM Perspective

Three basic operations performed by garbage collectors are - finding unused objects, making their memory available, and compacting the heap. Different collectors take different approaches to these operations, which is why they yield different performance characteristics.

It is simpler to perform these operations if no application threads are running while the garbage collector is running. Java programs are typically heavily multithreaded, and the garbage collector itself often runs multiple threads. So there are two logical groups of threads: those performing application logic, and those performing GC.

The pauses when all application threads are stopped are called stop-the-world pauses. These pauses generally have the greatest impact on the performance of an application, and minimizing those pauses is the key consideration when tuning GC.

With little bit of difference in their implementation almost all garbage collectors work by splitting the heap into different generations. They are broadly divided into two generations – old and young generation. The young generation is further divided into eden and the survivor spaces. Java garbage collector is designed to take advantage of the fact that most of the objects in the memory have very short life. This is where the generational design comes in. Objects are first created in the young generation which is some subset of the entire heap. When the young generation fills up, the garbage collector will stop the application threads and empty out the young generation by either discarding or moving the objects elsewhere. This operation of cleaning up the young generation is called minor GC.

There are two advantages of this design. First, as the young generation is only a portion of the entire heap, processing it is faster than processing the entire heap. This results in shorter pause of the application thread but the downside is that the application threads will be stopped more frequently. But it is always a big advantage to have shorter pauses even though they will be more frequent. The second advantage is that when the young generation is cleared during the GC all objects in eden are either moved or discarded; all live objects are moved to either one of the survivor spaces or to the old generation. Since all objects are moved, the young generation is automatically compacted when it is collected. All GC algorithms have stop the world pauses during the collection of young generation.

As objects are moved from young generation to old generation eventually it will also fill up and the JVM will need to find the objects that are no longer in use and discard them from the old generation. This is where the GC algorithms have their biggest differences. The simpler algorithms stop all application threads, find the unused objects and free their memory, and then compact the heap. This process is called a full GC, and it generally causes a long pause for the application threads.

It is also possible to find unused objects while application threads are running; CMS and G1 both take that approach. Because the phase where they scan for unused objects can occur without stopping application threads, CMS and G1 are called concurrent collectors. They are also called low-pause (and sometimesincorrectlypauseless) collectors, since they minimize the need to stop all the application threads. Concurrent collectors also take different approaches to compacting the old generation. With CMS or G1 collector, an application will typically experience fewer and much shorter pauses. The trade-off is that the application will use more CPU overall.

Different GCs suit for different application needs. There are trade-offs in every situation. In an application such as a Java EE server measuring the response time of individual requests, consider these points:

 The individual requests will be impacted by pause timesand more importantly by long pause times for full GCs. If minimizing the effect of pauses on response times is the goal, a concurrent collector will be more appropriate. If the average response time is more important than the outliers (i.e., the 90th% response time), the throughput collector will usually yield better results. The benefit of avoiding long pause times with a concurrent collector comes at the expense of extra CPU usage. Similarly, the choice of garbage collector in a batch application is guided by the following trade-off: If enough CPU is available, using the concurrent collector to avoid full GC pauses will allow the job to finish faster. If CPU is limited, then the extra CPU consumption of the concurrent collector will cause the batch job to take more time.

No comments:

Post a Comment