[PATCH v6] malloc: Mark pages with MADV_DONTNEED to shrink and grow rather than mremap()
Wilco Dijkstra
Wilco.Dijkstra@arm.com
Wed Sep 10 13:35:13 GMT 2025
Hi William,
+static __always_inline bool
+_int_realloc_madvise (mchunkptr oldp, size_t difference)
+{
+ /* Only MADV_DONTNEED if allowing overcommit memory, otherwise there
+ is a risk of exhausting the process' pre-determined VMA fraction. */
+ if (__glibc_unlikely (!check_can_keep_vma ()))
+ return false;
I don't believe we need this check - it's never wrong to use madvise.
+ char *madv_start = (char *) mmap_base (oldp) + mmap_size (oldp) - difference;
+ return __madvise (madv_start, difference, MADV_DONTNEED) != -1;
+}
Only this part needs to check !check_may_shrink_heap():
+ difference = ALIGN_DOWN (difference, GLRO (dl_pagesize));
+ /* Don't shrink more than the relative threshold for the chunk. */
+ if (difference <= max_madvise (mmap_size (oldp)) &&
+ _int_realloc_madvise (oldp, difference))
+ return oldmem;
+ }
Then if mremap fails/doesn't exist, we still use madvise since that does reduce
actual memory usage.
+/* When disabling overcommit behavior, we want to make sure that we are
+ judicious with our heap usage as well, and explicitly give away the freed
+ top of the heap to reduce our commit charge. */
+static inline bool
+check_may_shrink_heap (void)
+{
+ static int may_shrink_heap = -1;
+
+ return may_shrink_heap >= 0 || check_can_keep_vma ();
}
This removes the caching of the expensive systemcall... It may be worth renaming
to something more useful if we can think of a better name.
Note + return !overcommitted; incorrectly flips the existing meaning of true/false.
Florian: I am not sure what the __libc_enable_secure has to do with overcommit - so
I am wondering whether we could remove that check altogether?
Note there is an existing bug in that it is not thread-safe:
static inline bool
check_may_shrink_heap (void)
{
static int may_shrink_heap = -1;
if (__builtin_expect (may_shrink_heap >= 0, 1)) // should use __glibc_likely
return may_shrink_heap;
may_shrink_heap = __libc_enable_secure; // first write
if (__builtin_expect (may_shrink_heap == 0, 1)) // spurious read (may not return same value as __libc_enable_secure)
{
int fd = __open_nocancel ("/proc/sys/vm/overcommit_memory",
O_RDONLY | O_CLOEXEC);
if (fd >= 0)
{
char val;
ssize_t n = __read_nocancel (fd, &val, 1);
may_shrink_heap = n > 0 && val == '2'; // 2nd write
__close_nocancel_nostatus (fd);
}
}
return may_shrink_heap; // spurious read (may return different value from last write)
}
Basically one can do one read of the global and one write if the read says it is not
initialized - otherwise you end up with glitches and incorrect values returned.
Cheers,
Wilco
More information about the Libc-alpha
mailing list