Skip to content

Commit 3ee7cfd

Browse files
committed
DES-7259 Documented how to tune JVM memory.
Explained why JVM memory tuning might be needed, what parameters are user-adjustable and how to deal with memory managemend problems.
1 parent cc1bbbb commit 3ee7cfd

7 files changed

Lines changed: 157 additions & 0 deletions

File tree

content/en/docs/deployment/private-cloud/private-cloud-deploy.md renamed to content/en/docs/deployment/private-cloud/private-cloud-deploy/_index.md

File renamed without changes.
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
---
2+
title: "Tuning JVM memory settings for a Mendix app running in Mendix on Kubernetes"
3+
linktitle: "JVM memory tuning"
4+
url: /developerportal/deploy/private-cloud-jvm-memory-tuning/
5+
description: "Describes how to tune a Mendix app's JVM memory settings in Mendix on Kubernetes"
6+
weight: 10
7+
---
8+
## Introduction
9+
10+
Mendix Runtime and any Mendix apps run in a Java Virtual Machine.
11+
12+
Most Java Virtual Machines (including the one used in Mendix on Kubernetes) are based on the HotSpot Java VM.
13+
The HotSpot VM uses a garbage collector to free up unused memory.o
14+
15+
Running the garbage collector causes a negative performance impact; the Hotspot VM tries to only run garbage collection when absolutely necessary (for example, when memory usage reaches a certain threshold).
16+
17+
{{% alert color="info" %}}
18+
For a comprehensive guide explaining how the HotSpot VM performs garbage collection, see the [official Java documentation](https://docs.oracle.com/en/java/javase/21/gctuning/index.html).
19+
{{% /alert %}}
20+
21+
## Memory tuning
22+
23+
In most cases, Mendix on Kubernetes doesn't need advanced memory tuning.
24+
Starting with Operator version 2.26, the default memory settings have been aligned with Mendix Cloud and are based on typical Mendix app usage patterns.
25+
26+
Manual adjusting JVM memory usage is only needed in some cases - when the default memory settings leave unused memory.
27+
28+
### Types of memory
29+
30+
In the HotSpot VM, memory is split into two regions, **Heap** and **Non-Heap**.
31+
32+
* **Heap** memory is used to store temporary _data_, including microflow variables, entities and internal state of the Mendix Runtime and third-party libraries.
33+
* **Non-Heap** memory stores _code_, internal Java VM data and data from non-Java components, such as the [BAPI Connector](/appstore/modules/sap/sap-bapi-connector/#setup-environment) or machine learning modules from [ML Kit](/refguide/machine-learning-kit/).
34+
35+
There's a fixed boundary between **Heap** and **Non-Heap** memory: any memory reserved for the **Heap** cannot be used for any other purpose; all remaining memory will be assigned to the **Non-Heap** region.
36+
37+
{{< figure src="/attachments/deployment/private-cloud/private-cloud-deploy/jvm-memory-overview.png" class="no-border" >}}
38+
39+
It's not possible to dynamically adjust the amount of **Heap** and **Non-Heap** regions of a running app, this needs to be specified before starting an app.
40+
41+
### Default memory allocation{#default-memory-allocation}
42+
43+
Starting with Operator version 2.26, the default memory settings use the following rules:
44+
45+
| Container memory limit | JVM non-heap size |
46+
|------------------------|-------------------|
47+
| Less than 2 GB | Half of the limit (but at least 300 MB) |
48+
| Between 2 and 4 GB | 1 GB |
49+
| Between 4 and 8 GB | 1.5 GB |
50+
| Between 8 and 16 GB | 2 GB |
51+
| Between 16 and 32 GB | 3 GB |
52+
| 32 GB or more | 4 GB |
53+
54+
All other memory is allocated to the heap.
55+
56+
{{% alert color="info" %}}
57+
Older Operator versions don't set any memory configuration.
58+
In this case, the HotSpot VM allocates 25% of the memory limit to heap memory, and the rest is assigned to non-heap memory.
59+
This assignment is not optimal, as typical Mendix apps primarily benefit from heap memory.
60+
{{% /alert %}}
61+
62+
### Metrics-based tuning
63+
64+
{{% alert color="info" %}}
65+
Instead of freeing memory, the HotSpot VM prefers to recycle/reuse it.
66+
When looking at total memory usage in a container (for example using `kubectl top pods` or data collected by `kube-state-metrics`),
67+
the total memory usage will not reflect internal details.
68+
69+
For example, the JVM **Heap** might be underutilized, but will show as "used memory" in the container memory usage.
70+
71+
To have a clear picture of how memory is allocated and used, use Prometheus metrics.
72+
{{% /alert %}}
73+
74+
For examples how to read JVM memory usage graphs, see the [Java Memory Usage](/refguide/java-memory-usage/) document.
75+
76+
### Adjusting memory in Mendix on Kubernetes
77+
78+
In a [Standalone environment](/developerportal/deploy/private-cloud-operator/#edit-cr), the JVM heap size can be adjusted in the `jvmMemorySettings` section of the `MendixApp` CR.
79+
80+
For example:
81+
82+
```yaml
83+
apiVersion: privatecloud.mendix.com/v1alpha1
84+
kind: MendixApp
85+
metadata:
86+
# ...
87+
# omitted lines for brevity
88+
# ...
89+
spec:
90+
# ...
91+
# omitted lines for brevity
92+
# ...
93+
# Add or update this section:
94+
jvmMemorySettings:
95+
heapLimit: 1700Mi # example: set heap limit to 1700 megabytes
96+
```
97+
98+
Removing the `jvmMemorySettings` section will switch to the [default memory allocation](#default-memory-allocation) settings.
99+
100+
<!-- TODO: show how to adjust memory in Portunus when DES-7177 is merged -->
101+
102+
Starting with Operator version 2.26, the JVM heap size should be adjusted as documented in this section.
103+
If you have an environment using any of the following custom JVM options, they _will not be applied_ to the app configuration (will be removed):
104+
105+
* `-Xms` - minimal heap size
106+
* `-Xmx` - heap size limit
107+
* `-XX:MinRAMPercentage` - heap size limit, in percentages for environments with less than 250MB of container memory
108+
* `-XX:MaxRAMPercentage` - heap size limit, in percentages for environments with more than 250MB of container memory
109+
* `-XX:InitialRAMPercentage` - minimal heap size, in percentages
110+
111+
## Addressing memory issues
112+
113+
On a typical desktop system and many server environments, reaching or exceeding the memory limit is not a problem.
114+
115+
The operating system will move some of the memory contents to a storage device such as an SSD or hard drive.
116+
However, this will likely cause a major performance penalty, and in Kubernetes is [disabled by default](https://kubernetes.io/docs/concepts/cluster-administration/swap-memory-management/).
117+
118+
When an app tries to use more memory than it's container memory limit, it will be terminated by Kubernetes (OOMKilled) and will effectively crash.
119+
120+
To prevent performance degradation and the app from crashing, it might need its memory to be adjusted.
121+
122+
### Addressing OOMKilled crashes
123+
124+
An OOMKilled (Out Of Memory-killed) event happens when an app attempts to use more memory than specified in its container limit:
125+
126+
{{< figure src="/attachments/deployment/private-cloud/private-cloud-deploy/jvm-nonheap-full.png" class="no-border" >}}
127+
128+
This will show as a container restarting with an `OOMKilled` exit code:
129+
130+
{{< figure src="/attachments/deployment/private-cloud/private-cloud-deploy/jvm-memory-oomkilled.png" class="no-border" >}}
131+
132+
To address this issue:
133+
134+
* Recommended fix: increase the core resource memory (_requests_ and _limits_).
135+
* Alternatively, if the Prometheus metrics show that the heap memory usage is low: decrease the heap memory size.
136+
137+
{{% alert color="info" %}}
138+
This can also happen if a node is overcommitted (not enough available memory for app pods).
139+
140+
To prevent overcommitment, adjust the memory _requests_ to match the container _limits_.
141+
{{% /alert %}}
142+
143+
### Addressing java.lang.OutOfMemoryError exceptions
144+
145+
A `java.lang.OutOfMemoryError` exception happens when the JVM **Heap** is full and doesn't have enough available memory to perform an action (such as a Microflow or a Java Custom Action).
146+
147+
{{< figure src="/attachments/deployment/private-cloud/private-cloud-deploy/jvm-heap-full.png" class="no-border" >}}
148+
149+
This will show as a `java.lang.OutOfMemoryError: Java heap space` error message in the logs.
150+
151+
To address this issue:
152+
153+
* Recommended fix: increase the core resource memory (_requests_ and _limits_).
154+
* Alternatively, if the container has a lot of free, unused memory: increase the heap memory size.

content/en/docs/deployment/private-cloud/private-cloud-operator.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ spec:
8484
cpu: 250m
8585
memory: 256Mi
8686
ephemeral-storage: 256Mi
87+
jvmMemorySettings: # Optional, can be omitted: set custom Java VM memory configuration
88+
heapLimit: 100Mi # set heap limit to 100 megabytes
8789
runtimeDeploymentPodAnnotations: # Optional, can be omitted : set custom annotations for Mendix Runtime Pods
8890
# example: inject the Linkerd proxy sidecar
8991
linkerd.io/inject: enabled
@@ -195,6 +197,7 @@ You must make the following changes:
195197
* **certificate** and **key** - Not recommended for production environments. Provide the `tls.crt` and `tls.key` values directly. Cannot be used together with **secretName**.
196198
* **replicas** - By default, one replica is started when you deploy your app.
197199
* **resources** - Change the minimum and maximum container resources your app requires.
200+
* **jvmMemorySettings** - Change the JVM memory configuration. For more information, see [JVM memory tuning](/developerportal/deploy/private-cloud-jvm-memory-tuning/).
198201
* **serviceAnnotations** - Set custom annotations for network Services. These annotations are applied on top of [default annotations](/developerportal/deploy/private-cloud-cluster/#advanced-network-settings) from `OperatorConfiguration`.
199202
* **endpointAnnotations** - Set custom annotations for Ingress (or OpenShift Route) objects. These annotations are applied on top of [default annotations](/developerportal/deploy/private-cloud-cluster/#advanced-network-settings) from `OperatorConfiguration`.
200203
* **ingressPath** - Specify a custom Ingress path. This overrides the [default ingress path](/developerportal/deploy/private-cloud-cluster/#advanced-network-settings) from `OperatorConfiguration`.
46.8 KB
Loading
186 KB
Loading
40 KB
Loading
54.1 KB
Loading

0 commit comments

Comments
 (0)