Summary
Add Linux-specific jcmd to trim the native C-heap.
Problem
When returning memory back to the glibc C-heap using free(3)
, that memory is often retained instead of being returned to the Operating System. The glibc is somewhat notorious for this behavior, whereas other libc-implementations release memory more promptly. This effect depends on allocation pattern, but usually the finer granular allocations are, the more pronounced is this effect. As a result the JVM process could experience permanent increases in working set size due to temporary spikes in C-heap usage.
Beside the obvious disadvantage of using too much memory it also confuses support engineers: faced with a JVM showing a high working set size, they cannot know if that footprint is really memory used by this process, or if part of that memory consists of unused glibc C-heap. Even though they can see how much C-heap the JVM thinks it uses (using NMT - and beware the sometimes considerable glibc-overhead), if NMT does not show anything untoward nothing is answered.
Solution
A new Linux-specific command is introduced to the jcmd command collection. That command would trigger a call to malloc_trim(3)
call inside the JVM process when running on glibc. In the future this could be expanded to other libc
environments if needed and if they provide a suitable API.
malloc_trim(3)
is a glibc-specific API to explicitly trim its C-heap [1]. When this API is called, the glibc attempts to reduce the C-heap footprint by shrinking the process heap (decreasing the brk) and discard dirty unneeded pages using madvise(2)
. This usually has immediate effect and releases superfluous memory to the Operating System.
Being able to manually trigger malloc_trim(3)
would have the following advantages:
- obviously it reduces memory pressure as a stop gap measure
- when analyzing cases of high memory footprint, it would allow to distinguish "real" footprint from cases where the glibc just holds on to memory. This is especially important for cases where the C-heap spike was used by code outside the JVM itself and therefore does not show up on NMT.
Specification
The proposed command takes the form:
jcmd System.trim_native_heap
and has no arguments.
Description: Attempts to free up memory by trimming the native C-heap
When running under glibc, calling it would cause malloc_trim(3)
to be executed synchronously. jcmd
will wait for the trim to finish and return with information about how much memory had been freed by this operation.
When running on non-glibc platforms (e.g. with muslc on Alpine), the command prints "Not available".
For example:
thomas@starfish:~$ jcmd 18770 System.trim_native_heap
18770:
Attempting trim...
Done.
Virtual size before: 28849744k, after: 28849724k, (-20k)
RSS before: 8685896k, after: 920740k, (-7765156k) <<<<
Swap before: 0k, after: 0k, (0k)
A preliminary RFR (which may be adapted depending on the outcome of this CSR) can be found here: [4]
[1] https://man7.org/linux/man-pages/man3/malloc_trim.3.html
[2] https://bugs.ruby-lang.org/issues/15667
[3] https://github.com/openjdk/jdk/pull/4510#issuecomment-864762379
- csr of
-
JDK-8268893 jcmd to trim the glibc heap
- Resolved