Performance and price are two big considerations in application hosting that always matter. And, often, we question ourselves on how to decrease the spends, without affecting the performance of applications at the same time. In this article, we’d like to address automatic memory management for Java applications hosted with Jelastic PaaS using garbage collection.
Let’s clarify what garbage collection is, what it does for Java applications and how it works within Jelastic PaaS.
Java Garbage Collection Overview
Garbage Collection is a form of automatic memory management. Its aim is to find data objects in memory that are no longer demanded and make their memory space available for reuse.
A created data object uses some memory that remains allocated until there are references for the use of the object. When there are no references for an object, it is considered to be no longer required and the memory occupied by the object can be reclaimed. In such a way, you don’t pay for unused resources and can cut your costs.
Jelastic tested different kinds of Garbage Collectors and defined the most appropriate ones for Java applications hosted in Jelastic Multi-Cloud PaaS, taking into consideration the automatic vertical scaling that Jelastic PaaS provides. As a result of the investigations, Jelastic adjusted the default settings of Garbage Collectors in Jelastic PaaS in order to increase the benefits for Jelastic PaaS users.
Jelastic PaaS supports the following Garbage Collectors (GCs):
- G1 GC (-XX:+UseG1GC) – is a default Garbage Collector in Jelastic PaaS. The Garbage-First (G1) is a server-style Garbage Collector for multiprocessor machines with a large amount of memory. The heap is partitioned into fixed-sized regions and G1 tracks the live data in those regions. When Garbage Collection is required, it collects from the regions with less live data first;
- Shenandoah GC (-XX:+UseShenandoahGC) – is a concurrent garbage collector for the JVM. Concurrent means that the GC tries to perform most of the activities in parallel without interrupting application performance. Such parallelism makes “stop-the-world” (STW) pauses extremely short that is the most required task for each GC. Another inherent advantage is an efficient work with small and large heaps with no impact on STW pauses’ length. Note that for using this GC, you need to unlock experimental options with -XX:+UnlockExperimentalVMOptions;
- ZGC (-XX:+UseZGC) – is low latency scalable garbage collector. Designed for use with applications that require a large heap and low latency. It uses a bunch of one generation and performs most (but not all) garbage collection in parallel with uninterrupted application work. This greatly limits the impact of garbage collection on your application response time. Note that before calling this GC type, you need to unlock experimental options with -XX:+UnlockExperimentalVMOptions;
- Epsilon GC (-XX:+UseEpsilonGC) – is a passive GC that handles memory allocation and doesn’t clear it when objects are no longer used. When your application exhausts the Java heap, the JVM goes down. EpsilonGC prolongs an application life until the memory will run out and dumps the memory. This can be useful for application memory usage debugging, as well as measuring and managing application performance. Note that before calling this GC type, you need to unlock experimental options with -XX:+UnlockExperimentalVMOptions;
- Parallel
- ParNew GC (-XX:+UseParNewGC) – is a “stop-the-world” multi-threaded Garbage Collector. Mostly it is aimed to collect young generation objects. Since young generation is normally small in size, ParNew GC does collection very fast and does not impact your application too much. In addition, ParNew GC has compaction of unused RAM that enables support of automatic vertical scaling – one of the prominent Jelastic PaaS features;
- Parallel GC (-XX:+UseParallelGC) – is used when the parallel collection method is required over young generation only. It cannot be applied along with ConcMarkSweep GC simultaneously unlike ParNew GC;
- Parallel Old GC (-XX:+UseParallelOldGC) – utilizes a parallel “mark-and-compact” algorithm which catches all application threads and then handles labeling and subsequent compaction with multiple garbage collector threads;
- ConcMarkSweep GC (-XX:+UseConcMarkSweepGC) – is designed for applications that prefer shorter garbage collection pauses and which can afford to share processor resources with the garbage collector while the application is running. It makes sense to use such a collector when applications requirements for time garbage collection pauses are low;
- Serial GC (-XX:+UseSerialGC) – performs garbage collection in a single thread and has the lowest consumption of memory among all GC types but, at the same time, it makes long pauses that can lead to application performance degradation.
Default JVM Options in Jelastic PaaS
By default Jelastic PaaS uses G1 GC for JVM 8+ versions. For lower versions it employs the ParNew GC. For JVM versions below 12, Jelastic PaaS also attaches jelastic-gc-agent.jar (https://github.com/jelastic-jps/java-memory-agent) which enables vertical scaling for older releases.
For JVM 12+ versions, Jelastic PaaS provides integrated vertical scaling to ensure G1 triggering with the following pre-set container variables:
- G1PERIODIC_GC_INTERVAL=3000 – interval between garbage collection in milliseconds (15 minutes by default);
- GC_SYS_LOAD_THRESHOLD_RATE=0.3 – custom multiplier to adjust the G1PeriodicGCSystemLoadThreshold value;
- G1PERIODIC_GC_SYS_LOAD_THRESHOLD={CPU_cores_number}*GC_SYS_LOAD_THRESHOLD_RATE – activates garbage collection if the average one-minute system load is below the set value, and this condition is ignored if set as zero.
You can always check the current settings of your Java process by executing the command ps -ax | grep java. You will see something like this:
/usr/java/libericajdk-12.0.1/bin/java.orig -server -XX:G1PeriodicGCSystemLoadThreshold=0.6 -XX:G1PeriodicGCInterval=900k -XX:+UseStringDeduplication –
XX:+UseG1GC -Xmaxf0.3 -Xminf0.1 -Xmx1638M -Xmn30M -Xms32M -jar jelastic-helloworld-1.1.war
Also Jelastic PaaS automatically configures the following parameters:
- Xmx – 80% of total available RAM in the container;
- Xms – 32 MB;
- Xmn – 30 MB.
If the JVM version is higher than 12, Jelastic PaaS additionally configures the following Java options:
- G1PeriodicGCSystemLoadThreshold=CPU_COUNT*0.3 – 30% of load average based on the number of CPU cores available in the container;
- G1PeriodicGCInterval=900k – 15 minutes should pass since any previous garbage collection paused.
For more details, you can review the following script that manages the automatic configuration of Java options (https://github.com/jelastic-jps/java-memory-agent/blob/master/scripts/memoryConfig.sh).
Customization of Garbage Collection Settings in Jelastic PaaS
If you believe that customization of default settings can improve performance or memory consumption, you can tune them according to the requirements of your application. We recommend customizing these configurations only if you fully understand the impact of such changes on your application behavior.
You can set a custom GC parameter based on your application requirements via Environment Variables (please do not mix them with Java options).
- _JAVA_OPTIONS and JAVA_TOOL_OPTIONS – Java options can be used for changing the default GC type, for example _JAVA_OPTIONS=”-XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC” (read more about these options);
- GC_DEF – type of Garbage Collector, for example GC_DEF=G1GC;
- XMX_DEF_PERCENT – RAM percentage to be provided as XMX, for example XMX_DEF_PERCENT=80;
- XMX_DEF (or just XMX) – maximum size for the Java heap memory, for example XMX_DEF=1638;
- XMS_DEF (or just XMS) – initial java heap size, for example XMS=32M;
- XMN_DEF – heap size for the young generation, for example XMN=30M;
- G1PERIODIC_GC_INTERVAL (for openJDK 12 / 13 only) – frequency of the G1 Periodic Collection in milliseconds (G1PeriodicGCInterval – 15 minutes by default), set as 0 to disable, G1PERIODIC_GC_INTERVAL=900;
- G1PERIODIC_GC_SYS_LOAD_THRESHOLD (for openJDK 12 / 13 only) – allows G1 Periodic Collection execution, if the average one-minute system load is below the set value, this condition is ignored if set as zero, and by default, it is equal to the {CPU_cores_number}*{GC_SYS_LOAD_THRESHOLD_RATE};
- GC_SYS_LOAD_THRESHOLD_RATE (for openJDK 12 / 13 only) – custom multiplier to adjust the G1PeriodicGCSystemLoadThreshold value (0.3 by default), for example G1PERIODIC_GC_SYS_LOAD_THRESHOLD_RATE=0.3;
- FULL_GC_AGENT_DEBUG – enables (true) or disables (false) the debug mode to track the Java GC processes in the logs, for example, FULL_GC_AGENT_DEBUG=true;
- FULL_GC_PERIOD – interval (in seconds) between the full GC calls (900 by default), for example FULL_GC_PERIOD=900;
- MAXPERMSIZE – automatically defined only for those Java containers which run a JVM version lower than 8 and with an allocated amount of RAM greater than 800 MB. In all other cases (i.e., if the container scaling limit is less than 7 cloudlets or it uses Java 8) this parameter is omitted. The actual value of the MaxPermSize setting is calculated based on Xmx memory amount divided by ten, but cannot be set greater than maximum of 256 MB, for example MAXPERMSIZE=163;
- XMINF_DEF – controls the minimum free space in the heap and instructs the JVM to expand the heap if it does not have at least XMINF_DEF value of free space after performing garbage collection, for example XMINF_DEF=0.1;
- XMAXF_DEF – controls how the heap is expanded and instructs the JVM to compact the heap if the amount of free space exceeds the XMAXF_DEF value, for example XMAXF_DEF=0.3.
Alternatively, these parameters can be passed to the Java process via the variables.conf file in the container.
All paths to config, executable or log files can differ based on the Java server you use and can be accessed via the Configuration File Manager or SSH.
Java Application Server | Path to variables.conf |
---|---|
Tomcat / TomEE | /opt/tomcat/conf/variables.conf |
GlassFish | /opt/glassfish/glassfish/domains/domain1/config/variables.conf |
Spring Boot | /home/jelastic/conf/variables.conf |
WildFly | /opt/wildfly/conf/variables.conf |
Payara | /opt/payara/glassfish/domains/domain1/config/variables.conf |
SmartFoxServer | /opt/repo/versions/2X/SmartFoxServer_2X/SFS2X/config/variables.conf or /opt/shared/smartfox/2X/SmartFoxServer_2X/SFS2X/config/variables.conf |
Java Engine | /home/jelastic/conf/variables.conf |
Jetty | /opt/jetty/etc/variables.conf or /opt/shared/conf/etc/variables.conf |
1. Open Config to configure your Java application server.
2. For Tomcat, navigate to the opt > tomcat > conf > variables.conf file.
3. In the opened variables.conf file you can override garbage collector default settings or even add another GC to replace the default one (G1). For example, if you want to use ShenandoahGC instead, simply add it to the variables.conf file as stated in the example below:
-XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC
4. After updating the garbage collector settings, only the specified garbage collector will be used while starting your Java application server (without taking into consideration the amount of allocated resources).
5. You can also control how JVM handles its heap memory with other JAVA options stated in this file.
-Xmx2048m -Xms32m
As a result of the properly configured options, the garbage collector can be observed in action via the Statistics tab.
That’s it! Enjoy resource efficiency while running your Java applications in the cloud. Try it yourself for free at our Jelastic PaaS platform.