std/gc
import std/gc to use it. Source: src/std/gc.iyi at v0.18.0.
The prelude allocates through the owned collector (arenas, roots, marker, sweep) and never asks the program to think about it. This module is for the program that wants to: hand out raw words (malloc, malloc_atomic, realloc, free), run a collection now (collect), hold the trigger back for a stretch (disable/enable), ask whether a pointer names a live chunk (is_heap_ptr), and read the numbers the collector actually keeps (stats).
The numbers are named for what they are, not for what another runtime would call them: mapped_bytes is what the arenas and large mappings reserve from the machine, not bytes in use; bytes_since_collection is the allocation word the trigger reads and every collection resets; bytes_freed_by_last_sweep and bytes_kept_by_last_sweep are the last sweep's tally; collections is how many have run. There is no lifetime total, no free-list size and no heap ceiling to set, because the collector keeps none of those, and a field of zeros would be a lie.
This module speaks for the default collector only. A build with -Dgc_none (no collector), -Dgc_boehm (a borrowed one) or a wasm32 target (a different heap) is refused here, at compile time, with the sentence below, rather than answering zeros or failing on a name the other heap does not have.
GCstruct
pub struct GC
# What the collector can answer, read at one instant.
struct Stats
# Bytes the last sweep handed back, and bytes it found still live.
def bytes_freed_by_last_sweep : UInt64
def bytes_kept_by_last_sweep : UInt64
# Bytes allocated since the last collection: the word the allocation
# trigger reads, folded in per cache slice and reset by every
# collection.
def bytes_since_collection : UInt64
# How many collections have run.
def collections : UInt64
def initialize(mapped_bytes : UInt64, bytes_since_collection : UInt64, bytes_freed_by_last_sweep : UInt64, bytes_kept_by_last_sweep : UInt64, collections : UInt64)
# Bytes the arenas and large mappings hold from the machine. A
# reservation, counted in whole mappings; not bytes in use.
def mapped_bytes : UInt64
def to_s : String
end
# Runs one collection now.
def self.collect : Nil
# The allocation trigger stops running collections until `enable`.
def self.disable : Nil
# The allocation trigger runs collections again. `collect` still works
# while disabled.
def self.enable : Nil
# Hands a chunk back before the sweep would. A null pointer is nothing
# to free.
def self.free(pointer : ::Pointer(Void)) : Nil
def initialize
# Whether `pointer` points into a live chunk on the collector's heap:
# its start or any byte inside it. Null, a stack address and a freed
# chunk answer false.
#
# Under the runtime lock, as `stats` is: a word in no arena walks the
# large-mapping list, and another thread's `free` or sweep unlinks and
# unmaps those nodes under that lock. Walked without it, beside three
# threads whose large chunks come and go, two seconds of `is_heap_ptr`
# died of a memory fault in 10 runs of 10 (`bench/std_gc_exercise.iyi`).
def self.is_heap_ptr(pointer : ::Pointer(Void)) : Bool
# `size` zeroed bytes the collector scans for pointers. A negative size
# is refused by name.
def self.malloc(size : Int) : ::Pointer(Void)
# `size` bytes the collector does not scan: for bytes that hold no
# pointers, so the marker skips them. They are not cleared - a chunk the
# sweep handed back comes out with its last owner's bytes, as Boehm's
# `GC_malloc_atomic` hands them out - so the caller writes every byte
# it reads. This said "zeroed" while 2000 chunks taken after a
# collection freed 2000 filled ones came back dirty, every one.
def self.malloc_atomic(size : Int) : ::Pointer(Void)
# The chunk at `pointer`, resized to `size` bytes; the answer may be a
# new address, with the old bytes copied.
def self.realloc(pointer : ::Pointer(Void), size : Int) : ::Pointer(Void)
# The numbers the collector keeps, read now.
#
# The walk holds the runtime lock, which a collection takes before it
# stops anything and holds while the scavenge unlinks an arena or a
# large mapping. Walked without it, a collection another thread's
# allocation started could stop this walk on a mapping, unlink and unmap
# it, and the walk's next read was of memory gone: beside three threads
# allocating, two seconds of `stats` died of a memory fault in 10 runs
# of 10 (`bench/std_gc_exercise.iyi`).
def self.stats : Stats
end
iyi:
GC— the program's window onto iyi's own collector.