In this article, Ram Lakshmanan shares a few tips to tune G1 Garbage collector to obtain optimal performance that are simple, yet effective. Tune your application to get optimal performance with these tips, methods, and explanations. G1 has been the default GC algorithm since Java 9.
G1 GC is an adaptive garbage collection algorithm that has become the default GC algorithm since Java 9. We would like to share a few tips to tune G1 Garbage collector to obtain optimal performance.
Consider passing ‘-XX:MaxGCPauseMillis’ argument with your preferred pause time goal. This argument sets a target value for maximum pause time. G1 GC algorithm tries it’s best to reach this goal.
SEE ALSO: Garbage Collection tuning & troubleshooting crash course
Avoid setting the young generation size to a particular size (by passing ‘-Xmn, -XX:NewRatio’ arguments). G1 GC algorithm modifies young generation size at runtime to meet its pause-time goals. If the young generation size is explicitly configured, then pause time goals will not be achieved.
When you are moving from other GC algorithms (CMS, Parallel, …) to G1 GC algorithm, remove all the JVM arguments related to the old GC algorithm. Typically passing old GC algorithms arguments to G1 will have no effect, or it can even respond in a negative way.
Because of inefficient programming practices, modern applications waste a lot of memory. Here is a case study showing memory wasted by the Spring Boot framework. One of the primary reasons for memory wastage is the duplication of string. A recent study indicates that 13.5% of application’s memory contains duplicate strings. G1 GC provides an option to eliminate duplicate strings when you pass the ‘-XX:+UseStringDeduplication’ argument. You may consider passing this argument to your application if you are running on Java 8 update 20 and above. It has the potential to improve the overall application’s performance. You can learn more about this property in this article.
For tuning purposes, in the below table, we have summarized important G1 GC algorithm arguments and their default values:
One of the effective ways to optimize G1 GC performance is to study the causes triggering the GC and provide solutions to reduce them. Here are the steps to study the GC causes.
1. Enable GC log in your application. It can be enabled by passing following JVM arguments to your application during startup time:
Up to Java 8:
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:{file-path}
From Java 9 and above:
-Xlog:gc*:file={file-path}
{file-path}: is the location where GC log file will be written.
2. You can analyze the GC log file using free tools like GCeasy, Garbage Cat, HP Jmeter. These tools report the reasons that are triggering the GC activity. Below is the GC Causes table generated by GCeasy tool when G1 GC Log file was uploaded. A full analysis report can be found here.
Fig: G1 GC causes (excerpt from GCeasy report)
Below are the solutions to address each of them.
Full GC – Allocation failures happen for two reasons:
Here are the potential solutions to address this problem:
When you see G1 Evacuation pause, then G1 GC does not have enough memory for either survivor or promoted objects or both. The Java heap cannot expand since it is already at its max. Below are the potential solutions to fix this problem:
Any object that is more than half a region size is considered a “Humongous object”. If the regions contain humongous objects, space between the last humongous object in the region and the end of the region will be unused. If there are multiple such humongous objects, this unused space can cause the heap to become fragmented. Heap fragmentation is detrimental to application performance. If you see several Humongous allocations, please increase your ‘-XX:G1HeapRegionSize’. The value will be a power of 2 and can range from 1MB to 32MB.
When ‘System.gc()’ or ‘Runtime.getRuntime().gc()’ API calls are invoked from your application, stop-the-world Full GC events will be triggered. You can fix this problem through the following solutions:
This might be a traditional method , but it works. Search in your application code base for ‘System.gc()’ and ‘Runtime.getRuntime().gc()’. If you see a match, then remove it. This solution will work if ‘System.gc()’ is invoked from your application source code. If ‘System.gc()’ is going to be invoked from your 3rd party libraries, frameworks, or through external sources then this solution will not work. In such circumstances, you can consider using the option outlined in #b and #c.
You can forcefully disable System.gc() calls by passing the JVM argument –‘XX:+DisableExplicitGC’ when you launch the application. This option will silence all the ‘System.gc()’ calls that are invoked from your application stack.
You can pass ‘-XX:+ExplicitGCInvokesConcurrent’ JVM argument. When this argument is passed, GC collections run concurrently along with application threads to reduce the lengthy pause time.
SEE ALSO: Garbage collection log analysis complements APM tools
If your application is using RMI, you can control the frequency in which ‘System.gc()’ calls are made. This frequency can be configured using the following JVM arguments when you launch the application:
-Dsun.rmi.dgc.server.gcInterval=n
-Dsun.rmi.dgc.client.gcInterval=n
The default value for these properties in:
JDK 1.4.2 and 5.0 is 60000 milliseconds (i.e. 60 seconds)
JDK 6 and later release is 3600000 milliseconds (i.e. 60 minutes)
You might want to set these properties to a very high value so that it can minimize the impact.
For more details about ‘System.gc()’ calls, and it’s GC impact you can refer to this article.
‘Heap dump Initiated GC’ indicates that heap dump was captured from the application using tools like jcmd, jmap, profilers,… Before capturing the heap dump, these tools typically trigger full GC, which will cause long pauses.. We shouldn’t be capturing heap dumps unless there is an absolute necessity.
We hope you find this article useful. Best wishes to tune your application to get optimal performance.