Tuning Configs of Cloud Foundry’s Java Buildpack to Avoid Out-of-Memory Errors

by Andrey BushikJuly 6, 2016
This tutorial overviews how to override configurations either with an environment variable or via creating a custom buildpack.

Java apps created in accordance with 12-factors are supposed to run in Cloud Foundry without significant modifications. However, sometimes the defaults provided by the public buildpacks don’t work. In this post, we will look at two ways to resolve out-of-memory errors.


Memory allocation in a Java buildpack

Most Java applications do not need any additional memory configuration to run in a Cloud Foundry environment. In general, it is enough to set the MEMORY_LIMIT environment variable or provide a manifest.yml file with a required value.

MEMORY_LIMIT specifies how much memory will be allocated for a container that will run the application. A Java buildpack uses this value to control the Java Runtime Environment’s (JRE) use of various regions of memory: heap, metaspace, native, and stack.

Memory allocated for these regions is calculated by the memory calculator, using memory-related properties of the Java buildpack.

tuning-cloud-foundry-java-buildpack-to-avoid-out-of-memory-errorsEmploying memory-related properties to calculate allocation

By default, the Java buildpack specifies the following values for memory distribution.

  • heap: 75%
  • metaspace: 10%
  • native: 10%
  • stack: 5%

Check out the readme file of the OpenJDK JRE for an explanation of Java Runtime Environment’s memory sizes and weights, as well as how the Java buildpack calculates and allocates memory to the JRE for your app.

When a Java application is deployed and the native memory is not sufficient, the container will restart before it does its first full garbage collection. This is a clear sign of insufficient allocation of native memory.

There are two ways to solve this issue: a complicated one and a simple one.


Solution #1: Creating a custom buildpack

The first (complicated) way is to create a custom buildpack with updated memory-related JRE options and specify this buildpack in the deployment manifest. Cloud Foundry docs provide exhaustive instructions on how to do that.

This approach has some serious flaws. Creating and maintaining a separate custom version of the Java buildpack just for a single application is not very efficient. It is also far from being trivial.


Solution #2: Overriding buildpack configuration

The second, simpler approach is to override buildpack configuration with an environment variable. The name of the variable needs to match the configuration file you want to override, but without the .yml extension and with the JBP_CONFIG prefix. The value of the variable must be a valid inline YAML.

In case of a memory-heuristics property in open_jdk_jre.yml, the variable might look like in the sample below.

JBP_CONFIG_OPEN_JDK_JRE: '[memory_calculator: {memory_heuristics: {heap: 65, metaspace: 20}}]'

It can be passed to Cloud Foundry directly.

cf set-env [APPLICATION_NAME] JBP_CONFIG_OPEN_JDK_JRE: '[memory_calculator: {memory_heuristics: {heap: 65, metaspace: 20}}]’

Note that you will have to run this command each time the app is redeployed. To avoid that and make the environment reproducible, specify the variable in the application’s manifest.

    JBP_CONFIG_OPEN_JDK_JRE: '[memory_calculator: {memory_heuristics: {heap: 65, metaspace: 20}}]'

This way, the environment variables won’t get lost if the app gets deployed elsewhere. Don’t forget to restage the app after editing the files.

In a Cloud Foundry environment where apps are automatically restarted, you may not even notice that there is an issue with memory allocation—unless you are specifically looking for it. So, make sure the heap and native memory spaces are sized appropriately for the app. Specifying environment variables in application’s manifest should be the most efficient way of doing that.


About the author

Andrei Bushyk is Senior Software Developer with hands-on experience in delivering multi-tier applications. He is profoundly knowledgeable about all aspects of a software development life cycle. Andrei’s technical proficiency enables him to accomplish high-level tasks, as well as efficiently perform tech lead and team lead functions. He also has expertise in such big data technologies as Cloudera, Apache Hadoop, Apache Crunch, and Apache Kafka. Andrei is a Sun Certified specialist for the Java platform.


Further reading

This post was written by Andrei Bushyk and edited by Alex Khizhniak.