Overview
This document provides a short overview of the recommended, default, GC settings for JVM applications in Banno. These settings should work well for the vast majority of use cases, but there will be cases when you will need to deviate from them.
When using these settings, it is strongly recommended that you are using JVM 11 or greater.
Switching to these settings from our previous setup has been shown to decrease memory be 30-70%. Application performance has been generally unaffected. The few cases where we did observe changes to application performance resulted in a performance improvement, albeit a small one.
TL;DR
These are the options you should set,
-XX:MaxRAMPercentage=90.0 -XX:MaxHeapFreeRatio=10 -XX:MinHeapFreeRatio=5
These will work for all JDKs >= 8.
If you are using banno-sbt-plugin ensure you are on version >= 11.0.0.
Environments K8s Setup
You should set an explicit memory value, as well as JAVA_TOOL_OPTIONS in the environment. JAVA_TOOL_OPTIONS is where you will put your GC (and other) JVM options.
Here is the YAML for an example app. This is probably not the full configuration that you will use, as you might set other environment variables or different CPU/Memory for your specific app.
apiVersion: deployables.banno.com/v1alpha1
kind: App
metadata:
creationTimestamp: null
name: example
spec:
cpu: 200m
memory: 400Mi
env:
- name: JAVA_TOOL_OPTIONS
value: >
-XX:MaxRAMPercentage=90.0
-XX:MaxHeapFreeRatio=10
-XX:MinHeapFreeRatio=5
image: docker.artifactory.banno-tools.com/example:1.0.0
livenessProbe:
failureThreshold: 3
httpGet:
path: /health
port: health
initialDelaySeconds: 60
ports:
- containerPort: 8080
name: health
- containerPort: 8081
name: http
readinessProbe:
failureThreshold: 5
httpGet:
path: /ping
port: http
initialDelaySeconds: 60
replicas: 3
Important, if you are using this setup do not use the jvm settings in the YAML from banno-k8s-operator. These settings replace those.
Considerations
Using these JVM settings will have a number of secondary implications on your app. For instance, the value of Runtime.getRuntime.availableProcessors will probably be different. Before, it was hard coded to yield 8 in production. Now, the JVM will infer it from the container environment. For example, in our YAML configuration we gave 200m cpu to the app, or 20% of a single CPU. This will cause the JVM to report that there is a single CPU in the system.
If you use this value to set different concurrency values you may be tempted to set -XX:ActiveProcessorCount= to override how many CPUs the JVM reports. Do not do this. Changing that value can cause the JVM’s heuristics to make poor decisions about which values to set for derived settings. A better and more safe approach would be derive concurrency values, with a fallback minimum value, e.g. max(32L, 8 * availableProcessors).
FAQ
- Which GC will we actually be using?
- That depends! Using these options you don’t explicitly set a GC. This allows the JVM to pick one for you based off of the environment in which it is run. For example, in many smaller apps, < 500MiB RAM and < 500m CPU, the JVM will pick the SerialGC. The SerialGC is a very simple single threaded GC. One might be tempted to change this to something like the G1GC or at least the ParallelGC thinking that the concurrent nature of these GCs would be better than the single threaded SerialGC, but this will often lead to poorer application performance and/or a worse memory footprint. The reason for this is that concurrent systems have non-trivial overhead in order to coordinate the multiple threads. This overhead is worth it for applications with relatively larger requirements, but for many smaller applications the overhead is more costly than the advantage of the concurrent GC.
-Xmxisn’t specified. How much heap will I have?- That depends! Oracle documentation recommends that you don’t explicitly specify a heap size for the default use
case. When it is omitted, the JVM will dynamically increase/decrease heap size as needed, as well as tune the other memory
parameters. There is a performance cost to increasing or decreasing the heap size, but in practice many applications
will quickly arrive at a steady state for heap usage, after which time they generally don’t need to increase or decrease
their heap. There are exceptions to this, such as applications which have in heap caches. In such cases, you may need to
deviate from these recommended defaults. This may mean explicitly setting a min/max heap size, or more generally changing
the values for
MaxHeapFreeRatioandMinHeapFreeRatioto make heap size adjustments occur less often.
- That depends! Oracle documentation recommends that you don’t explicitly specify a heap size for the default use
case. When it is omitted, the JVM will dynamically increase/decrease heap size as needed, as well as tune the other memory
parameters. There is a performance cost to increasing or decreasing the heap size, but in practice many applications
will quickly arrive at a steady state for heap usage, after which time they generally don’t need to increase or decrease
their heap. There are exceptions to this, such as applications which have in heap caches. In such cases, you may need to
deviate from these recommended defaults. This may mean explicitly setting a min/max heap size, or more generally changing
the values for
- What do these JVM options do?
-XX:MaxRAMPercentage=90.0- This tells the JVM to only use at most 90% of the available RAM for the main process. The other 10% is reserved for running the JVM itself, as well as running other processes. 90% would be high in a traditional system, but for Banno our processes are running in a container.
-XX:MaxHeapFreeRatio=10and-XX:MinHeapFreeRatio=5- These determine the maximum and minimum percentage of free space in the heap. If free space becomes < 10%, then the JVM will increase the heap size, if it is > 5% it will decrease it. These values were taken from the Oracle docs which note that for many applications even extremely conservative values such as 10% and 5% will not result in application runtime performance issues.
More Information
The settings recommended here were chosen after consulting these articles. I highly recommend reading them.