How to use ulimit with java correctly?

tombrown52 :

My java program must run in an environment where memory is constrained to a specified amount. When I run my java service it runs out of memory during startup.

This is an example of the commands I'm using and values I'm setting:

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

In theory, I've accounted for everything I could find documentation on. There's the heap (1000m), and the metaspace (500m). But it still runs out of memory on startup initializing the JVM. This happens when until I set the ulimit about 600mib larger than heap+metaspace.

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

Use case: I am running a task in a Docker container with limited memory. That means that linux cgroups is doing the limiting. When memory limits are exceeded, cgroups can only either pause or kill the process that exceeds it's bounds. I really want the java process to gracefully fail if something goes wrong and it uses too much memory so that the wrapping bash script can report the error to the task initiator.

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

Update: It does not die with an OutOfMemoryError. This is the error:

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

It's really hard to effectively ulimit java. Many pools are unbounded, and the JVM fails catastrophically when an allocation attempt fails. Not all the memory is actually committed, but much of it is reserved thus counting toward the virtual memory limit imposed by ulimit.

After much investigation, I've uncovered many of the different categories of memory java uses. This answer applies to OpenJDK and Oracle 8.x on a 64-bit system:

Heap

This is the most well understood portion of the JVM memory. It is where the majority of your program memory is used. It can be controlled with the -Xmx and -Xms options.

Metaspace

This appears to hold metadata about classes that have been loaded. I could not find out whether this category will ever release memory to the OS, or if it will only ever grow. The default maximum appears to be 1g. It can be controlled with the -XX:MaxMetaspaceSize option. Note: specifying this might not do anything without also specifying the Compressed class space as well.

Compressed class space

This appears related to the Metaspace. I could not find out whether this category will ever release memory to the OS, or if it will only ever grow. The default maximum appears to be 1g. It can be controlled with the '-XX:CompressedClassSpaceSize` option.

Garbage collector overhead

There appears to be a fixed amount of overhead depending on the selected garbage collector, as well as an additional allocation based on the size of the heap. Observation suggests that this overhead is about 5% of the heap size. There are no known options for limiting this (other than to select a different GC algorithm).

Threads

Each thread reserves 1m for its stack. The JVM appears to reserve an additional 50m of memory as a safety measure against stack overflows. The stack size can be controlled with the -Xss option. The safety size cannot be controlled. Since there is no way to enforce a maximum thread count and each thread requires a certain amount of memory, this pool of memory is technically unbounded.

Jar files (and zip files)

The default zip implementation will use memory mapping for zip file access. This means that each jar and zip file accessed will be memory mapped (requiring an amount of reserved memory equal to the sum of file sizes). This behavior can be disabled by setting the sun.zip.disableMemoryMapping system property (as in -Dsun.zip.disableMemoryMapping=true)

NIO Direct Buffers

Any direct buffer (created using allocateDirect) will use that amount of off-heap memory. The best NIO performance comes with direct buffers, so many frameworks will use them.

The JVM provides no way to limit the total amount of memory allowed for NIO buffers, so this pool is technically unbounded.

Additionally, this memory is duplicated on-heap for each thread that touches the buffer. See this for more details.

Native memory allocated by libraries

If you are using any native libraries, any memory they allocate will be off-heap. Some core java libraries (like java.util.zip.ZipFile) also use native libraries that consume-heap memory.

The JVM provides no way to limit the total amount of memory allocated by native libraries, so this pool is technically unbounded.

malloc arenas

The JVM uses malloc for many of these native memory requests. To avoid thread contention issues, the malloc function uses multiple pre-allocated pools. The default number of pools is equal to 8 x cpu but can be overridden by setting the environment variable MALLOC_ARENAS_MAX. Each pool will reserve a certain amount of memory even if it's not all used.

Setting MALLOC_ARENAS_MAX to 1-4 is generally recommend for java, as most frequent allocations are done from the heap, and a lower arena count will prevent wasted virtual memory from counting towards the ulimit.

This category is not technically it's own pool, but it explains the virtual allocation of extra memory.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=445488&siteId=1