-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JDK-8293114: JVM should trim the native heap #14781
JDK-8293114: JVM should trim the native heap #14781
Conversation
👋 Welcome back stuefe! A progress list of the required criteria for merging this PR into |
@tstuefe The following labels will be automatically applied to this pull request:
When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing lists. If you would like to change these labels, use the /label pull request command. |
15a33f4
to
c7093a5
Compare
Are you talking about allocations into native memory that a Java application does on its own accord and not as a consequence of the JVM doing its own allocs? For compiling, for example. |
This does not matter. The resulting malloc load is the sum of whatever we do and whatever the native code does. |
Webrevs
|
I don't think the comments from my yesterday's review were addressed :) There are some old comments (marked with |
Of course, sorry. Seems I missed most of them. About Pause in all VM ops, an alternative would be to just check in the trimmer if we are at safepoint, and if yes treat it as pause. I'll see if that's easier (I'm worried about pulling a mutex or atomic increasing the pauser variable in every VM op we run). |
Oh yes, I like it. We can just check |
@jdksjolen @shipilev New version:
@shipilev : I kept the SuspendMark inside TrimNative, because I like it that way. Otherwise, I would name it something like TrimNativeSuspendMark, so nothing gained but another symbol at global scope. |
The description says |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another read. I think we would need to tighten up style a bit too: the newline spacing style is different across the change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, looks good to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test needs a fix for non-Linux.
Yes, I am not saying we should do it in this PR. Just thinking ahead on interaction with manual trim. |
Well, yes. Isn't that what my patch did? |
I thought you wanted to collect overarching stats about how many trims got delayed. I see now you wanted to do something much simpler. Okay. |
Hi @dholmes-ora, @shipilev, New version:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am okay with this, with only minor nits left. Good job!
ml.notify_all(); // pause end | ||
} | ||
} | ||
log_debug(trimnative)("Trim resumed after %s (%u suspend requests)", reason, n); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you do it like I did in https://github.com/openjdk/jdk/files/12043977/trimnative-shipilev-2.patch ?
We don't say "resumed" if it really want not resumed due to non-zero suspend count.
} | ||
} | ||
|
||
log_trace(trimnative)("Times %u suspended, %u timed, %u safepoint", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Times:
, I think.
Thanks a lot @shipilev! Fixed the last nits, and fixed an issue where the "this platform does not support..." text was not displayed with warning level and hence not visible without Xlog. Plus, test for that. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still okay with it.
@dholmes-ora Are you okay with this final version? Did your CI report any problems? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing further from me.
Thanks
I'll wait for any last remarks until tomorrow morning (CET). Barring any objections, I'll push. |
Many thanks @robehn, @shipilev and @dholmes-ora ! Feels good to have this finally in. /integrate |
Going to push as commit 9e4fc56.
Your commit was automatically rebased without conflicts. |
@tstuefe unfortunately the test is failing intermittently in our CI. I will file a bug and assign it to you. Likely we will need a quick ProblemListing though. Thanks |
This is a continuation of #10085. I closed #10085 because it had accumulated too much comment history and got confusing. For a history of this issue, see previous discussions [1] and the comment section of 10085.
This RFE adds the option to trim the Glibc heap periodically. This can recover a significant memory footprint if the VM process suffers from high-but-rare malloc spikes. It does not matter who causes the spikes: the JDK or customer code running in the JVM process.
Background:
The Glibc is reluctant to return memory to the OS. Temporary malloc spikes often carry over as permanent RSS increase. Note that C-heap retention is difficult to observe. Since it is freed memory, it won't appear in NMT; it is just a part of RSS.
This is, effectively, caching - a performance tradeoff by the glibc. It makes a lot of sense with applications that cause high traffic on the C-heap. The JVM, however, clusters allocations and often rolls its own memory management based on virtual memory for many of its use cases.
To manually trim the C-heap, Glibc exposes
malloc_trim(3)
. With JDK 18 [2], we added a new jcmd command to manually trim the C-heap on Linux (jcmd System.trim_native_heap
). We then observed customers running this command periodically to slim down process sizes of container-bound jvms. That is cumbersome, and the JVM can do this a lot better - among other things because it knows best when not to trim.GLIBC internals
The following information I took from the glibc source code and experimenting.
Why do we need to trim manually? Does the Glibc not trim on free?
Upon
free()
, glibc may return memory to the OS if:a) for the main arena, glibc attempts to lower the brk()
b) for mmap-ed heaps, glibc attempts to completely unmap or shrink the heap.
In both cases, (a) and (b), only the top portion of the heap is reclaimed. "Holes" in the middle of other in-use chunks are not reclaimed.
So: glibc may automatically reclaim memory. In normal configurations, with typical C-heap allocation granularity, it is unlikely.
To increase the chance of auto-reclamation happening, one can do one or more things:
glibc.malloc.trim_threshold
to a very low value, e.g., 1But:
The JVM only has limited influence on (a), none on (b), (c) is a really bad idea, and hence (d) often does little. That mirrors my practical experiences.
How does
malloc_trim()
differ from trimming on free() ?malloc_trim()
, will look for holes that are larger than a page; so it limits itself not to just reclaiming memory at the top of the arena. It will thenmadvise(MADV_DONTNEED)
those holes. It does that for every arena.What are the cons of calling
malloc_trim()
?malloc_trim()
cannot be interrupted. Once it runs, it runs. The runtime ofmalloc_trim()
is not predictable. If there is nothing to reclaim, it is very fast (sub-ms). If there is a lot to reclaim (e.g. >32GB), I saw times of up to 800ms.Moreover,
malloc_trim
, while trimming each arena, locks the arena. That may lock out concurrent C-heap operations in the thread that uses this arena. Note, however, that this is rare since many operations will be satisfied from the tcache and therefore don't lock.What about the
pad
parameter formalloc_trim()
I found it has very little effect. It only affects how many bytes are preserved at the top of the main arena. It does not affect other arenas, nor does it affect how much space malloc_trim reclaims by releasing "holes", which is the main part of memory release.
The Patch
Patch adds new options (experimental):
GCTrimNativeHeap
is off by default. If enabled, it will cause the VM to trim the native heap periodically. The period is defined byGCTrimNativeHeapInterval
.Periodic trimming is done in its own thread. We cannot burden the ServiceThread, since the runtime of trims is unpredictable.
The patch also adds a way to suspend trimming temporarily; if suspended, no trims will start, but ongoing trims will still finish.
The patch uses this mechanism to suspend trimming during GC STW phases and whenever we are about to do bulk C-heap operations (e.g. deleting deflated monitors).
Examples:
This is an artificial test that causes two high malloc spikes with long idle periods.
(yellow) NMT shows two spikes for malloc'ed memory;
(red) RSS of the baseline JVM shows that we reach a maximum and then never recover. This is the glibc retaining the free'd memory.
(blue) RSS of the patched JVM shows that we recover RSS in steps by doing periodic C-heap trimming.
(See here for parameters: run script )
Tests
Tested older Glibc (2.31), and newer Glibc (2.35) (
mallinfo()
vsmallinfo2()
), on Linux x64.Older versions of this patch were routinely tested at SAP for almost half a year.
Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/14781/head:pull/14781
$ git checkout pull/14781
Update a local copy of the PR:
$ git checkout pull/14781
$ git pull https://git.openjdk.org/jdk.git pull/14781/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 14781
View PR using the GUI difftool:
$ git pr show -t 14781
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/14781.diff
Webrev
Link to Webrev Comment