mirror of
https://github.com/ruby/ruby.git
synced 2026-10-28 16:36:20 +08:00
This patch allows the OS to free page and arena memory. This only happens in single objspace mode at the end of a major GC. There is still an empty_pages list per objspace, and this stays resident as before. Only pages previously munmapped pre-rlgc are madvised(DONTNEED)'d. We call these discarded pages. Once an arena (2MB) has all of its 32 pages (each 64K) discarded, we munmap the arena. At least 1 arena is kept mapped at all times. The following fields are added to GC.stat for the default GC: page_pool_arenas // number of mapped arenas page_pool_arenas_freed // number of freed (unmapped) arenas page_pool_total_pages // total pages in all arenas (arena_count * 32 currently) page_pool_discarded_pages // number of madvise(DONTNEED) pages (they can still be mapped) Future work: * Reduce the amount of empty pages per objspace or get rid of objspace->empty_pages altogether and keep a certain amount of resident pages in the pool for all objspaces. * Allow reclaiming memory when there are multiple Ractors.
96 lines
3.5 KiB
C
96 lines
3.5 KiB
C
#ifndef INTERNAL_VM_MAP_H
|
|
#define INTERNAL_VM_MAP_H
|
|
/**
|
|
* @author Ruby developers <ruby-core@ruby-lang.org>
|
|
* @copyright This file is a part of the programming language Ruby.
|
|
* Permission is hereby granted, to either redistribute and/or
|
|
* modify this file, provided that the conditions mentioned in the
|
|
* file COPYING are met. Consult the file for details.
|
|
* @brief Shared per-platform virtual memory mapping helpers.
|
|
*/
|
|
#include "ruby/internal/config.h"
|
|
#include <stddef.h> /* for size_t */
|
|
|
|
#ifdef HAVE_SYS_MMAN_H
|
|
# include <sys/mman.h>
|
|
#endif
|
|
#ifdef HAVE_ERRNO_H
|
|
# include <errno.h> /* for errno, EAGAIN */
|
|
#endif
|
|
|
|
// On Solaris, madvise() is NOT declared for SUS (XPG4v2) or later,
|
|
// but MADV_* macros are defined when __EXTENSIONS__ is defined.
|
|
#ifdef NEED_MADVICE_PROTOTYPE_USING_CADDR_T
|
|
#include <sys/types.h>
|
|
extern int madvise(caddr_t, size_t, int);
|
|
#endif
|
|
|
|
// Reduces RSS lazily if possible, otherwise does it immediately. Keeps the mapping.
|
|
static inline void
|
|
rb_vm_map_reusable_lazy(void *addr, size_t len, int advice)
|
|
{
|
|
#ifdef __wasi__
|
|
/* WebAssembly doesn't support madvise. */
|
|
#elif defined(VM_CHECK_MODE) && VM_CHECK_MODE > 0 && defined(MADV_DONTNEED)
|
|
if (!advice) advice = MADV_DONTNEED;
|
|
madvise(addr, len, advice);
|
|
#elif defined(MADV_FREE_REUSABLE)
|
|
/* Darwin / macOS / iOS. Drops phys_footprint; pages stay in resident_size
|
|
* until the kernel reclaims them. Retry on EAGAIN. */
|
|
if (!advice) advice = MADV_FREE_REUSABLE;
|
|
while (madvise(addr, len, advice) == -1 && errno == EAGAIN);
|
|
#elif defined(MADV_FREE)
|
|
/* Recent Linux. */
|
|
if (!advice) advice = MADV_FREE;
|
|
madvise(addr, len, advice);
|
|
#elif defined(MADV_DONTNEED)
|
|
/* Old Linux. */
|
|
if (!advice) advice = MADV_DONTNEED;
|
|
madvise(addr, len, advice);
|
|
#elif defined(POSIX_MADV_DONTNEED)
|
|
/* Solaris. */
|
|
if (!advice) advice = POSIX_MADV_DONTNEED;
|
|
posix_madvise(addr, len, advice);
|
|
#elif defined(_WIN32)
|
|
VirtualAlloc(addr, len, MEM_RESET, PAGE_READWRITE);
|
|
#endif
|
|
}
|
|
|
|
// Reduces RSS immediately if possible, otherwise does it lazily. Keeps the mapping.
|
|
static inline void
|
|
rb_vm_map_reusable_immediate(void *addr, size_t len, int advice)
|
|
{
|
|
#ifdef __wasi__
|
|
/* WebAssembly doesn't support madvise. */
|
|
#elif defined(MADV_FREE_REUSABLE)
|
|
/* The most aggressive option is MADV_FREE_REUSABLE — same as _lazy. It drops
|
|
* phys_footprint immediately. Retry on EAGAIN. */
|
|
if (!advice) advice = MADV_FREE_REUSABLE;
|
|
while (madvise(addr, len, advice) == -1 && errno == EAGAIN);
|
|
#elif defined(MADV_DONTNEED)
|
|
/* Linux: kernel discards physical pages immediately; next touch
|
|
* zero-faults and RSS drops at once. */
|
|
if (!advice) advice = MADV_DONTNEED;
|
|
madvise(addr, len, advice);
|
|
#elif defined(POSIX_MADV_DONTNEED)
|
|
if (!advice) advice = POSIX_MADV_DONTNEED;
|
|
posix_madvise(addr, len, advice);
|
|
#elif defined(_WIN32)
|
|
// Does not reduce working set immediately. We would need 2 system calls to do this.
|
|
VirtualAlloc(addr, len, MEM_RESET, PAGE_READWRITE);
|
|
#endif
|
|
}
|
|
|
|
/* Re-acquire pages previously given back. Only needed for certain platforms. */
|
|
static inline void
|
|
rb_vm_map_reuse(void *addr, size_t len)
|
|
{
|
|
#if defined(MADV_FREE_REUSE)
|
|
/* Darwin: mandatory handshake with MADV_FREE_REUSABLE. Retry on EAGAIN. */
|
|
while (madvise(addr, len, MADV_FREE_REUSE) == -1 && errno == EAGAIN);
|
|
#endif
|
|
/* Linux/Windows/wasi: no-op (fault re-materializes zero-filled pages). */
|
|
}
|
|
|
|
#endif /* INTERNAL_VM_MAP_H */
|