9

I have created some services in spring boot, I have 11 fat jars and I deploy them in docker containers, my doubt was that every jar was consuming between 1 and 1.5 GB of RAM without any use, I check the RAM by running:

docker stats containername

At first I thought that it was the java container and I tried to change to one that uses alpine but nothing changed, so I think the only problem is my jar. Is there a way to change the RAM that the jar is using? Or this behavior is normal because every jar has an embedded tomcat? Or maybe is better to put some jars together and deploy them as war and use only one tomcat for a group of "jars"? Can someone share his/her experience?,

Thanks in advance.

Ali Ben Zarrouk
  • 1,429
  • 12
  • 20
Alan Gaytan
  • 712
  • 2
  • 13
  • 28
  • which image are you running? Have you tried to check the memory consumed if you run it locally? – juanlumn Aug 09 '18 at 13:36
  • I'm running openjdk:8 and also I try with openjko:8u171-jdk-alpine3.8 but it doesn't help. And let me test it running locally – Alan Gaytan Aug 09 '18 at 13:47
  • I tested them locally and the same thing is happening – Alan Gaytan Aug 09 '18 at 13:57
  • May [this article](https://developers.redhat.com/blog/2017/03/14/java-inside-docker/) can give us some clarification about the issue – juanlumn Aug 09 '18 at 14:00
  • If you don't tell your JVM to limit its memory usage, it will use the defaults, which can be pretty large. And as long as there is memory free to use, your garbage collector probably won't do a thing, and even if it did, it's not always given back to the system. This is purely how Java works, and has nothing to do with Spring boot, nor is it a memory issue with Spring boot. – g00glen00b Aug 09 '18 at 14:03

3 Answers3

5

You can set memory usage of docker container using -e JAVA_OPTS="-Xmx64M -Xms64M".

docker file:

FROM openjdk:8-jre-alpine
VOLUME ./mysql:/var/lib/mysql
ADD /build/libs/application.jar app.jar
ENTRYPOINT exec java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar

image run:

 docker run -d --name container-name -p 9100:9100 -e JAVA_OPTS="-Xmx512M -Xms512M"   imagename:tag

Here i set 512Mb memory usage . you can set 1g or as per your requirement. After run using this check your memory usage. it will max 512Mb.

GolamMazid Sajib
  • 6,630
  • 4
  • 17
  • 32
  • Thanks, this solve my problem, the only thing is how can I know how much RAM my jar needs? – Alan Gaytan Aug 09 '18 at 15:24
  • @AlanGaytan By monitoring (eg. with jvisualvm). You can see how much memory your application uses and it can be used to manually trigger GC. Take a look at your memory after performing a GC during normal load, that is what your application at least requires to run. Make sure that you allow it to use more memory than that. Too little and it will either go out of memory, or continuously GC, causing performance issues. Too much and it won't GC soon enough, causing you to use too much memory. – g00glen00b Aug 10 '18 at 07:25
5

This is how Java behaves in general. The JVM takes as much memory as you give it, and it will perform a process called Garbage collection (What is the garbage collector in Java) to free up space once it decides it should do so.

However, if you don't tell your JVM how much memory it can use, it will use the system defaults, which depend on your systems memory and the amount of cores you have. You can verify this using the following command (How is the default Java heap size determined):

java -XX:+PrintFlagsFinal -version | grep HeapSize

On my machine, that's an initial heap memory of 256MiB and a maximum heap size of 4GiB. However, that doesn't mean that your application needs it.

A good way of measuring your memory is by using a monitoring tool like jvisualvm. Additionally, you could use actuator's /health endpoint to see the heap memory usage as well.

Your heap memory usage will normally have a sawtooth pattern (Why a sawtooth shaped graph), where the memory is gradually being used, and eventually freed by the garbage collector.

Example of JVisualVM

The memory that is left over after a garbage collection are usually objects that cannot be destroyed because they're still in use. You could see this as your working memory. Now, to configure your -Xmx you'll have to see how your application behaves after trying it out:

  • Configure it below your normal memory usage and your application will go out of memory, throwing an OutOfMemoryError.
  • Configure it too low but above your minimal memory usage, and you will see a huge performance hit, due to the garbage collector continuously having to free memory.
  • Configure it too high and you'll reserve memory you won't need in most of the cases, so wasting too much resources.

From the screenshot above, you can see that my application reserves about 1GiB of memory for heap usage, while it only uses about 30MiB after a garbage collection. That means that it has a way too high -Xmx value, so we could change it to different values and see how the application behaves.

People often prefer to work in powers of 2 (even though there is no limitation, as seen in jvm heap setting pattern). In my case, I need to go with at least 30MiB, since that's the amount of memory my application uses at all times. So that means I could try -Xmx32m, see how it performs, and adjust if it goes out of memory or performs worse.

g00glen00b
  • 34,293
  • 11
  • 80
  • 106
  • Thank you for your explanation! – sebasira Jul 28 '20 at 15:44
  • You are a life saver ! I have been struggling with my apps that kept growing their sizes reading thousands of posts here on SO. Your answer shed light into my issues, once I set the proper parameters xms and xms, I solved my problem... – Digao Mar 17 '21 at 22:41
1

After taking a look into the openjkd DockerHub image documentation it seems that you can set the Default Heap Size by setting -XX:MaxRAM=...:

RAM limit is supported by Windows Server containers, but currently JVM cannot detect it. To prevent excessive memory allocations, -XX:MaxRAM=... option must be specified with the value that is not bigger than a containers RAM limit.

From the oracle docs:

Default Heap Size Unless the initial and maximum heap sizes are specified on the command line, they are calculated based on the amount of memory on the machine.

juanlumn
  • 4,228
  • 2
  • 24
  • 30