- Managing Java Heap size in Kubernetes
- Problem statement
- Setup and how to replicate
- Scenario #1: Running a jdk version below 8u191 inside an unrestricted pod
- Scenario #2: Running a jdk version below 8u191 on a pod with a 1GB memory limit
- Scenario #3: Running jdk version 8u191 on a pod with a 1GB memory limit
- Scenario #4: Running a jdk version below 8u191 inside an unrestricted pod with JVM command-line option
- Alternative scenario — experimental parameter
- Conclusions/Recommendations
- linux-notes.org
- Узнать размер Java Heap Memory Size
- Обзор памяти Java
- ТЕСТЫ или ГОТОВЫЕ примеры
- Предлагаемые для Java Memory
- Вывод
Managing Java Heap size in Kubernetes
A guide on how to handle Java ( jdk8 ) heap size for containerised microservices in Kubernetes .
Problem statement
I have come across the topic for this article while managing Java microservices in a Kubernetes environment for a project where, due to restricted access to external software, we were limited to an older jdk version.
When you specify a pod, you can and should (assuming you are not experimenting on your personal workbench) specify how much of each resource a container needs. I am assuming the reader of this article will already have the understanding of allocating pod resources, but if that is not the case reference [3] specifies the differences between requests and limits and even presents implementation details.
It is possible to control the amount of memory your Java application uses with the Java command-line parameters -Xmx , -Xms and -Xss (thoroughly debated in reference [4]). When working in a non-development scenario, we want to avoid manual overriding Java options. The solution and conclusions discussed below provide a simplification of CI/CD implementation (also discussed below).
Getting back to what motivated this article, we found when using a jdk version below 8u191 to containerise Java microservices, the JVM does not pick up the quotas attributed to that container.
Consequently, before going any further, a disclaimer is required: if you are using a jdk version 8u191 or above, this article will probably not be the most relevant to you, but I always find this sort of non-perfect world scenarios quite useful. Additionally, please stick around for the recommendations section even if that is not your case.
Over the following subsections, I will present a couple of scenarios that illustrate the described topic, as well as a “how to replicate” piece.
Setup and how to replicate
If you want to verify the scenarios described below in your machine, you’ll need the following:
- Basic understanding of Docker and Java. Understanding of how to upgrade/downgrade Java versions.
- minikube installed (or access to any Kubernetes cluster);
- This git repository, which has the Kubernetes manifest files and a cheat sheet bash script (it will automatically replicate all the results presented below). The structure of this repository is illustrated below. You’ll be able to find the cheat sheet bash script right at the root directory and the Kubernetes manifest files under k8s/manifests .
You’ll need a way to evaluate the maximum Java heap size, which can be done using the following command:
As a baseline, running the above command in an unrestricted Docker container running in my local machine (similar to Scenario 1 below) yields 4198498304 Bytes , which is 4 Gigabytes , 25% of the memory allocated to my local Docker engine( 32GB ).
25% is the default RAM fraction for the Java maximum heap space (1/ MaxRAMFraction from the screenshot above). This fraction can be changed, which will be further discussed in the recommendations section below.
Scenario #1: Running a jdk version below 8u191 inside an unrestricted pod
In the first scenario, we are running jdk 8u181 and not imposing any limits.
As we may have been expecting, the result for the maximum Java heap space is 4198498304 Bytes (
4GB ), which is 25% of the total memory allocated to my Docker engine (which is running a Kubernetes cluster using minikube where this pod is deployed), as we are not imposing any quotas.
Scenario #2: Running a jdk version below 8u191 on a pod with a 1GB memory limit
When deploying to enterprise Kubernetes clusters, good practice dictates we should attribute quotas to our pods/deployments/. (sometimes this is even strongly enforced). To do that, we use a resources block in our Kubernetes manifest file:
In this second scenario (screenshot above), we are also running jdk 8u181 and, unfortunately, the JVM does not pick up the quotas attributed to the pod and maintains the same
4GB maximum heap size, which is what motivated this article.
Scenario #3: Running jdk version 8u191 on a pod with a 1GB memory limit
This scenario shows that this is not an issue if you are running jdk version 8u191 or above (again, please stick around for the recommendations section).
The next screenshot features a similar setup (resources block in the manifest file for a pod) but this time running jdk version 8u191 .
We can see that the JVM detects the limits correctly, attributing a Java maximum heap size of 25% of the memory available to the pod.
Scenario #4: Running a jdk version below 8u191 inside an unrestricted pod with JVM command-line option
If we go back to jdk 8u181 and have absolutely no option of upgrading, I found that the most reliable way to actually limit the heap size is to compute it and hardcode it into the Java command to run your application (could be your container entry point, your Kubernetes container command and arguments or something along those lines). In this exposé, I simulate that in the kubectl command I use to obtain the values, but in a Dockerfile it can be something like this:
We can once again conclude that it worked, the JVM is using the specified maximum heap size. However, this is a solution to avoid if possible (if you can upgrade your jdk ), as it means manual math and hardcoding, two very dangerous words in the DevOps world!
Alternative scenario — experimental parameter
If we take a look at reference [1], there is also mention of an alternative solution: from JDK 8u131+ , there’s an experimental VM option that allows the JVM ergonomics to read the memory values from CGgroups . I am not going to further detail this solution here but it may be worth taking a look at.
Conclusions/Recommendations
The first couple of scenarios illustrate my observations motivation for this article, while the remaining expose possible isolated solutions. After living through this issue, we were able to update to a later version of jdk8 . Even so, my recommendation is to go for a hybrid solution:
- In a containerised microservice context, using only 25% of the total container available memory for Java maximum heap size is a bit overkill, as the container will not have any other background activities apart from running the Java process and keeping itself alive (specific requirements may contradict this statement).
- There’s another Java option that we can use to select the maximum RAM fraction for the Java maximum heap size, the -XX:MaxRAMFraction Java option.
- The StackOverflow post in reference [2] quotes a RAM fraction of 50% is safe(ish) to start experimenting. We have been using this without any issues thus far.
- You may ask (the StackOverflow post author actually did) why not allocating 100% of the container available memory for the Java heap space. As is pictured in the cover illustration for this article, the JVM is never the only process running on a container. Moreover, the JVM memory usage is not 100% heap space.
- Changing the RAM fraction can be achieved also in the container entry point or in the Kubernetes command and arguments, something like:
- One key advantage of the solution discussed here is the simplification of CI/CD implementation in the terms that we won’t need to adjust the Java options or the requests/limit of Kubernetes resources.
Источник
linux-notes.org
Узнать размер Java Heap Memory Size
Есть проблема с Java Heap Memory Size, ее нужно нормально указать для того чтобы все нормально работало ( например, веб- сервер tomcat, liferay, OpenMRS и так далее) и в данной статье «Узнать размер Java Heap Memory Size» расскажу как можно найти оптимальное значение для java.
Сейчас я покажу вам, как использовать-XX:+PrintFlagsFinal чтобы узнать ваш размер кучи «heap size». В Java, по умолчанию и максимальный размер кучи распределяются на основании алгоритма — эргономики (ergonomics algorithm).
Как же рассчитывается Heap sizes?
Начальный размер кучи — это соотношение 1/64 физической памяти к 1 Гб. (Initial heap size of 1/64 of physical memory up to 1Gbyte)
Максимальный размер кучи — это соотношение 1/4 физической памяти до 1 Гб. (Maximum heap size of 1/4 of physical memory up to 1Gbyte)
Тем не менее, алгоритм что выше, как раз для справки, могут варьироваться в разных VM.
Обзор памяти Java
Краткий обзор структуры памяти Java:
1. Java Heap Size
Место для хранения объектов, созданных приложением Java, это где Garbage Collection берет память для приложений Java. Для «тяжелого процесса Java», недостаточный размер кучи вызовет популярную ошибку: java.lang.OutOfMemoryError: Java heap space.
-Xms — Установить начальный размер Java куча «Java heap size»
-Xmx — Установить максимальный размер Java куча «Java heap size»
2. Размер Perm Gen (Perm Gen Size).
Место для хранения определение загруженного класса и метаданных. Если большой кодовый, базовый проект загружен, имеет недостаточный размер Perm Gen size то это вызовит ошибку: Java.Lang.OutOfMemoryError: PermGen.
-XX:PermSize — Установить начальный размер для параметра «PermGen Size».
-XX:MaxPermSize — Установить максимальный размер для параметра «PermGen Size».
3. Размер java стека «Java Stack Size»
Размер java нитей (Size of a Java thread). Если проект имеет много обработки нитей, попробуйте уменьшить этот размер стека, чтобы избежать нехватки памяти.
-Xss = Установить размер стека Java (java thread stack size).
ЗАМЕТКА
Значение по умолчанию для heap size, perm gen, или stack size отличаются от различных JVM. Лучшие практически всегда определить собственные значение для сервера.
ТЕСТЫ или ГОТОВЫЕ примеры
Приведу различные тесты на разных ОС.
Ubuntu
Среда тестирования имеет такие характеристики:
ОС : Ubuntu 13 (64 битная) (Под управлением VirtualBox)
RAM : 4Гб оперативной памяти
CPU : 1 x Процессор
JDK : 1.7
Выполним команду для отображения необходимой информации:
Для JVM выделено следующие значения по умолчанию:
Java heap size
InitialHeapSize = 64781184 байт (61.7 Мб) и MaxHeapSize = 1038090240 байт (990Мб).
PermGen Size
PermSize = 21757952 байт (20.75 Мб), MaxPermSize = 174063616 байт (166 Мб)
Thread Stack Size
ThreadStackSize = 1024 кб (1 Мб)
Выделенный размер кучи (heap memory size) довольно близок к результатам эргономики.
#ergonomics algorithm
Initial heap size = 4096M/64 = 64 Мб
Maximum heap size = 4096M/4 = 1024 Мб
Mac OS X
Среда тестирования имеет такие характеристики:
ОС : Mac OSX 10.9
RAM : 8 Гб оперативной памяти
CPU : 4 x Поцессор
JDK : 1.7
Запускаем команду чтобы проверить:
Узнать размер Java Heap Memory Size в macOS X
Для JVM выделено следующие значения по умолчанию:
Java heap size
InitialHeapSize = 20655360 байт (19.69M) и MaxHeapSize = 331350016 bytes (316 Мб).
PermGen Size
PermSize = 21757952 байт (20.75 Мб), MaxPermSize = 85983232 байт (82 Мб).
Java Stack Size
ThreadStackSize = 1024 Кб (1 Мб)
Выделенный размер кучи памяти (heap memory size) не имеют никакого значения, если сравнить со следующий результат по алгоритму эргономики.
#ergonomics algorithm
Initial heap size = 8192M/64 = 128 Мб
Maximum heap size = 8192M/4 = 2048 Мб
Windows
Grep нет в Windows, вместо этого, мы используем Findstr.
Среда тестирования имеет такие характеристики:
ОС : Windows 8
RAM : 16 Гб
CPU : 8 x процессор
JDK : 1.7
Узнать размер Java Heap Memory Size в windows
Для JVM в Windows ОС выделено следующие значения по умолчанию:
Java heap size
InitialHeapSize = 266634176 байт (256 Мб) и MaxHeapSize = 4266146816 байт (4068 Мб).
PermGen Size
PermSize = 21757952 байт (20.75 Мб), MaxPermSize = 85983232 bytes (823 Мб).
Java Stack Size
ThreadStackSize = 0 kilobytes. (weird…)
Выделенный размер кучи памяти почти такой же как и по алгоритму эргономика:
#ergonomics algorithm
Initial heap size = 16384/64 = 256 Мб
Maximum heap size = 16384/4 = 4096 Мб
Предлагаемые для Java Memory
Ниже, я привел мою рекомендацию (параметры) для малой и средней нагрузки приложений Java 🙂
Heap = -Xms512m -Xmx1024m
PermGen = -XX:PermSize=64m -XX:MaxPermSize=128m
Thread = -Xss512k
P.S: Для большинства проектов Java, 512k памяти для потока (thread) более чем достаточно.
оптимаьные значения для Java Heap Memory Size
Часто задаваемые вопросы.
какая -version?
Избегайте жалоб от компилятора Java, заменить «-version» с вашим именем приложения Java.
Что это -XX:+PrintCommandLineFlags?
Это -XX:+PrintCommandLineFlags используется для вывода значения,только для изменения VM (обозначается так: = символ).
Вывод
Наконец, значения по умолчанию для heap memory, perm gem и stack size отличается от JVM, не ожидайте что JVM будет использовать оптимальные значения для вашего приложения Java.
На этом, я завершаю «Узнать размер Java Heap Memory Size». Надеюсь, было познавательно.
Источник