Misc #19122
openUse MADV_DONTNEED instead of MADV_FREE when freeing a Fiber's stack
Description
I'd like to propose that Ruby stops using MADV_FREE when freeing a Fiber's stack, and switches to using MADV_DONTNEED even when MADV_FREE is supported.
MADV_FREE is used in one place in the Ruby codebase, when freeing the stack of a freed Fiber: https://git.ruby-lang.org/ruby.git/tree/cont.c#n683
The comment for fiber_pool_stack_free
says:
// We advise the operating system that the stack memory pages are no longer being used.
// This introduce some performance overhead but allows system to relaim memory when there is pressure.
Where possible (i.e. on Linux 4.5 and later), fiber_pool_stack_free
uses MADV_FREE
over MADV_DONTNEED
. This has the side effect that memory statistics such as RSS will not reduce until and unless the OS actually reclaims that memory. If that doesn't happen, then the reported memory usage via RSS will be much higher than the 'real' memory usage.
If this was pervasive throughtout the Ruby codebase then that would be one thing, but currently this is just for Fiber. This means that:
- A program that doesn't use Fiber will have somewhat reliable RSS statistics on recent Linux.
- A program that heavily uses Fiber (such as something using Async::HTTP) will see an inflated RSS statistic.
Go made a similar change to the one I'm proposing here for similar reasons: https://github.com/golang/go/issues/42330
While
MADV_FREE
is somewhat faster thanMADV_DONTNEED
, it doesn't affect many of the statistics thatMADV_DONTNEED
does until the memory is actually reclaimed. This generally leads to poor user experience, like confusing stats intop
and other monitoring tools; and bad integration with management systems that respond to memory usage.
[...]
I propose we change the default to preferMADV_DONTNEED
overMADV_FREE
, to favor user-friendliness and minimal surprise over performance. I think it's become clear that Linux's implementation ofMADV_FREE
ultimately doesn't meet our needs.
As an aside, MADV_FREE was not used in Ruby 3.1 (https://bugs.ruby-lang.org/issues/19101), and I haven't found any bugs filed about this behaviour other than that one.