How to set memory limit for your Java containers?
By default, Docker containers have no resource constraints and can use as much as the host’s kernel memory and scheduler allows but there could be scenarios where you have to set it explicitly. For example in case if your container consumes too much of the host machine’s memory and if the kernel detects that there is not enough memory to perform system functions, the kernel would take over and start killing processes to free up memory.
This could effectively bring your application down, in this post we are going to look at how to set the memory limit for containers and in specific how to address the challenges in case of running Java applications on Docker containers.
Quick Snapshot
This quickstart assumes basic understanding of Docker concepts, please refer to earlier posts for understanding on Docker & how to install and containerize applications.
#1. Setting memory limit for individual containers
To limit memory for the container, we can use the --memory flag or just -m during the startup of the container. For example, we set the memory limit of NGINX server to only 256 MB of RAM.

To check the memory usage run docker stats command to check the same.

If you get below warning message during docker run command then cgroups swapping is disabled by default. cgroups (control groups) is basically a Linux kernel setting through which you can set limits the user accounts for the resource usage (CPU, memory, disk I/O, network, etc.) for a collection of processes. By this, you can isolate the processes and their resource usage from each other.
WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
For enabling swap, add/modify the below line in /etc/default/grub file and update grub configuration by sudo update-grub command.
GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"
For the changes to take effect, reboot the docker host machine.
If you have multiple containers and if you’re using compose to start the containers, follow the next section on how to set memory limits.
#2. Setting memory limit on Docker Compose
Below is the sample compose file for sample application with Angular as front end, Spring Boot as API, and Postgres as Database.
version: '3'
services:
ui:
build:
context: .
dockerfile: UIDockerfile
ports:
- '4200:4200'
networks:
- samplenet
links:
- 'api:api'
api:
build:
context: .
dockerfile: AppDockerfile
ports:
- '8080:8080'
depends_on:
- db
- rabbitmq
networks:
- samplenet
links:
- 'db:db'
db:
build:
context: .
dockerfile: DBDockerfile
volumes:
- 'postgresdb:/var/lib/postgresql/data'
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: testdb
ports:
- '5432:5432'
healthcheck:
test:
- CMD-SHELL
- 'pg_isready -U postgres'
interval: 10s
timeout: 5s
retries: 5
networks:
- samplenet
rabbitmq:
image: 'rabbitmq:3.5.3-management'
container_name: rabbitmq2
ports:
- '5672:5672'
- '15672:15672'
networks:
- samplenet
networks:
samplenet: null
volumes:
postgresdb: {}
Above compose file defines 4 services as below:
ui: this is for angular application,it exposes port4200on the container to port4200on the host machine.api:Â this is for spring boot application,it exposes port8080on the container to port8080on the host machine. Note this is dependent ondbandrabbitmqservices.db: this is for postgresdb application,it exposes port5432on the container to port5432on the host machine.rabbitmq: Usesrabbitmq:3.5.3-managementpublic image from Dockerhub registry and uses ports5672,15672- All the above services uses
samplenetnetwork and - Persistent volume
postgresdbdefinition is fordbservice - Added
healthchecksection for db service to keep tab on the health of the database depends_ondenotes the service dependencies. When you start the services, compose would start the dependent services as well.
For example, if you want to set memory limits for particular container say api services. We can use mem_limit option like below:
api:
build:
context: .
dockerfile: AppDockerfile
ports:
- '8081:8081'
depends_on:
- db
- rabbitmq
networks:
- samplenet
mem_limit: 1024MB
links:
- 'db:db'
Above option would work only if you’re using compose version 2.x starting from 3.x , compose is aligned with Swarm and you have to use resources option like below
api:
build:
context: .
dockerfile: AppDockerfile
ports:
- '8081:8081'
depends_on:
- db
- rabbitmq
networks:
- samplenet
resources:
limits:
memory:1024M
links:
- 'db:db'
Having seen how to set memory limits for containers, the next step is to address the memory-related challenges with respect to Java applications running on containers. I am sure, most of you who have running applications on Docker have faced Out of memory exceptions or improper heap memory issues. In the next section, we are going to check out how to resolve those issues.
Does JVM know whether it is running on container?
For setting memory limits, like in any conventional Java application the maximum Java heap size can be set explicitly via JVM command-line option -Xmx. When running applications on Docker, you can use the same command-line option to restrict the memory but consider the scenario that you have not mentioned any of the JVM command-line options, when a Java application is using Java SE 8u121 and earlier on Docker container, the JVM would automatically use the underlying host configuration of memory i.e., JVM would not know that its running on container and more likely your Java process would get killed if its taking too much memory.
Luckily from Java SE 8u131 and in later editions, JVM is made Docker aware with respect to Docker memory limits and starts to adjust like its running on the bare machine. To enable this, there are few command-line options.
-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1
If the above JVM command-line options are specified and -Xmx is not specified, the JVM will look at the Linux cgroup configuration for setting up the memory limits. With -XX:MaxRAMFraction=1 set to 1, we are instructing JVM to use almost all the available memory as max heap.
In the compose file, you can pass these options as an environment variable like in the example below :
environment:
- "JAVA_OPTS=-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1"

Next steps, we will check if JVM is able to detect how much memory is available when running inside a Docker container for different JDKs. We are going to run the container with 1GB memory and measure how much Max Heap is set.
#1.Open JDK 8

#2.Open JDK 9

#3.Open JDK 10
For JDK 10, -XX:MaxRAM parameter is deprecated and JVM will correctly detect the memory.

Congrats! Today we have learned how to set memory limit for your Java containers and also we have analyzed and set JVM environment variables for different JDKs.
Before we close off, let us look at key considerations while setting up memory.
Key considerations while setting memory
- Run performance/load tests to understand the memory requirements of your application and configure the memory limit values and they should be refined iteratively. If there are memory leaks, fix those ones before moving on to containers.
- Swap is slower and less performant than memory but it can provide a buffer if running out of memory.
- Use memory limits for your containers as explained above so that it does not overuse the host memory.
- Check container runtime metrics. Use
docker statscommand to analyze the container’s runtime metrics. It supports CPU, memory usage, memory limit, and network IO metrics. - Analyze cgroup metrics to understand the internals. In the case of Linux Containers, control groups are used to track groups of processes. CPU, memory, and block I/O usage metrics are exposed via pseudo-filesystem like
/sys/fs/cgroup.
Like this post? Don’t forget to share it!
Additional Resources:
- Full list of Compose commands
- Compose configuration file reference
- Most Popular courses of 2019
- Google Cloud Courses Collection
- Trending Skill: Deep Learning Course Collection
- Trending Skill: Python Curated Course Collection
- ULTIMATE GUIDE to Coursera Specializations That Will Make Your Career Better (Over 100+ Specializations covered)
- Ultimate Guide to Data Science Courses (Over 65+ courses covered)


Average Rating