How to use ulimit correctly with java?

My Java program needs to run in an environment where memory is limited to a certain amount. When I start my java service, it runs out of memory during startup.

This is an example of the commands I am using and the values ​​I am setting:

ulimit -Sv 1500000
java \
    -Xmx1000m -Xms1000m \
    -XX:MaxMetaspaceSize=500m \
    -XX:CompressedClassSpaceSize=500m \
    -XX:+ExitOnOutOfMemoryError \
    MyClass

      

In theory I have explained what I can find documentation for. There is a heap (1000 m) and a metapas (500 m). But on startup, the JVM initialization still ends. This happens when I set ulimit to about 600mib more than heap + metaspace.

What category of memory am I missing so that I can set ulimit appropriately?

Use case: I am running a task in a Docker container with limited memory. This means that the Linux groups are doing the restriction. When memory limits are exceeded, groups can only suspend or kill a process that exceeds its limits. I really want the java process to gracefully crash if something goes wrong and it uses too much memory for the bash script wrapper to be able to report the error to the task initiator.

We are using java 8, so we need to worry about metaspace instead of permgen.

Update: it doesn't die since OutOfMemoryError

. This is mistake:

Error occurred during initialization of VM
Could not allocate metaspace: 524288000 bytes

      

+3


source to share


1 answer


It's really tricky to effectively ulimit java. Many pools are unlimited and the JVM crashes when the allocation attempt fails. Not all memory is actually committed, but most of it is reserved, so counts against the virtual memory limit imposed by ulimit.

After much research, I discovered many of the different categories of Java memory usage. This answer applies to OpenJDK and Oracle 8.x on a 64-bit system:

Heap

This is the most understandable part of the JVM memory. This is where most of your program memory is used. It can be controlled with the -Xmx

and options -Xms

.

Metaspace

This appears to contain metadata about the classes loaded. I couldn't find out if this category will ever release memory to the OS, or if it will only grow. The default maximum is 1g . It can be controlled with an option -XX:MaxMetaspaceSize

. Note. Specifying this might do nothing without specifying the compressed class space.

Compressed class space

This is similar to Metaspace. I couldn't find out if this category will ever release memory to the OS, or if it will only grow. The default maximum is 1g . It can be controlled with the -XX: CompressedClassSpaceSize option.

Garbage collector overhead

It seems that the fixed amount of overhead depends on the garbage collector chosen as well as additional allocation based on heap size. Observation suggests that this overhead is around 5% of the heap size. There are no known options for limiting this (except for the choice of another GC algorithm).

Topics

Each thread reserves 1 meter for its own stack. The JVM seems to reserve an extra 50M of memory as a security measure against. The stack size can be controlled with the option -Xss

. There is no way to control the size of the security. Since there is no way to maximize the number of threads, and each thread requires a certain amount of memory, this memory pool is technically unlimited.

Jar files (and zip files)



By default, the zip implementation will use memory mapping to access the zip file. This means that every available jar and zip file will be mapped to memory (requires an amount of reserved memory equal to the sum of the file sizes). This behavior can be turned off by setting a system property sun.zip.disableMemoryMapping

(as in -Dsun.zip.disableMemoryMapping=true

)

NIO Direct Buffers

Any direct buffer (created with allocateDirect

) will use that amount of heap memory. Best Performance NIO comes with direct buffers, so many frameworks will use them.

The JVM does not allow for limiting the total amount of memory available for NIO buffers, so this pool is technically unlimited.

In addition, this memory is duplicated on the heap for each thread that touches the buffer. For details see.

Native memory allocated by libraries

If you use any native libraries, any memory they allocate will be inactive. Some core Java libraries (for example java.util.zip.ZipFile

) also use native libraries that use heap memory.

The JVM does not provide an option to limit the total memory allocated by the embedded libraries, so this pool is technically unlimited.

malloc arenas

The JVM uses malloc for many of these native memory requests. To avoid thread contention issues, malloc uses multiple pre-allocated pools. The default number of pools is 8 x cpu, but can be overridden by setting an environment variable MALLOC_ARENAS_MAX

. Each pool will reserve a certain amount of memory, even if not in use.

Setting MALLOC_ARENAS_MAX

to 1-4 is generally recommended for java as the most frequent allocations are done from the heap and a lower arena rating will prevent skipped virtual memory from counting to ulimit.

This category is technically not a native pool, but explains virtual allocation of additional memory.

+3


source







All Articles