This article will provide some tips on picking a memory limit for your Java and JVM based applications. This includes any application running with the Java Build Pack and even some that are derived from it.
The recommendation for sizing your application is to pick a memory limit for your Java or JVM based applications starting intentionally high. Then monitor your app's usage and gradually lower the limit until you reach a point where you're comfortable with the overhead and in being able to handle your peak usage. We recommend this approach because memory and monitoring are cheap, it's not a labor intensive process and it should keep your app up and running.
Picking a Memory Limit
Here are some things specific to the platform that you should consider when picking your memory limit.
- The memory limit is the total amount of memory allowed for use by the JVM's process. This includes the heap, PermGen or Metaspace, native space, memory for threads and an handful of other things. When thinking about your memory limit, make sure you take all of this into consideration.
- The memory enforcer on PWS is unforgiving and will kill your app if you exceed the memory limit by any amount. Because of this, you should build some overhead or margin for error into any calculations you make regarding your memory limit. This will help to minimize crashes and restarts of your app.
To help with your calculations, here are some common memory limits and a break down of how the memory is allocated for the JVM. These are based on the default rules in the Java Build Pack at the time this article was written, which are listed below.
- Heap will be 75% of your memory allocation.
- Meta space will be 10% of your memory allocation with a minimum of 64m.
- Native space is set to 10%.
- Stack is different, because -Xss is per thread and not a total memory limit. It's generally going to be set at or very close to 1M/thread.
Memory Limit - 512M
For an application with a 512M memory limit, the JVM will get roughly 373M for the heap, 64M for the PermGen/Metaspace and a 1M thread stack size. This leaves roughly 75M for native memory and thread stacks. At 1M per thread, that would give you at most 75 threads.
Memory Limit - 1G
For an application with a 1G memory limit, the JVM will get roughly 768M for the heap, 102M for the PermGen/Metaspace and a 1M thread stack size. This leaves roughly 154M for native memory and thread stacks. At 1M per thread, that would give you at most 154 threads.
Memory Limit - 2G
For an application with a 2G memory limit, the JVM will get roughly 1536M for the heap, 204M for the PermGen/Metaspace and a 1M thread stack size. This leaves roughly 308M for native memory and thread stacks. At 1M per thread, that would give you at most 308 threads.
Impact / Risk
This article focuses on the aspects of sizing your application that are specific to PWS. You should also take into consideration any normal best practices for sizing your Java & JVM based applications.
While the Java build pack does it's best to configure the JVM so that it does not exceed the memory limit you specify, there is no way to completely prevent your app from exceeding it's memory limit. The JVM does not allow you to cap the native or thread stack memory usage, which means that given the right conditions it could grow beyond the expected amounts causing you to exceed your memory limit.
For more details on how the Java build pack allocates memory for your application, see this link. If you would like to make adjustments to the way the build pack allocates memory by default, you can do so by setting specific environment variables (`cf set-env` or in your manifest.yml file). More information on this can be found here.
Please also note that to change the JVM options, you must restage the application. This is either done by running cf push or cf restage. It is not sufficient to run cf scale, cf restart or any other command that does not completely restage the application.