(sigil gc)
(sigil gc) - Garbage Collector Control
Control the garbage collector for diagnostics, manual collection, and fine-tuning for latency-sensitive applications like games.
(import (sigil gc))
;; Force immediate garbage collection
(gc-collect!)
;; Spread GC work across game frames
(define (game-loop)
(gc-step! 100) ; Process up to 100 objects per frame
(update)
(render)
(game-loop))
;; Check memory usage
(gc-allocated-bytes) ; => 1234567Exports
gc-collect!procedureForce a full garbage collection cycle.
Pauses execution until all unreachable objects are freed. Use sparingly—may cause noticeable pauses.
(gc-collect!)gc-minor-collect!procedureRun a minor (young-generation) collection now.
Collects only recently-allocated objects — typically a sub-millisecond pause, much cheaper than gc-collect!. Latency-sensitive programs (games) can call this at a convenient moment (e.g. end of frame) to keep allocation-triggered minor collections from landing mid-frame:
;; At end of each frame:
(when (> (gc-young-occupancy) 0.6)
(gc-minor-collect!))No-op on the marksweep GC backend (no young generation) and while a major collection cycle is in progress.
gc-frame-hint!procedureFrame-boundary collection hint for games.
Runs a minor collection now only if the young generation is at least threshold full (a fraction of the minor-GC trigger, the same scale as gc-young-occupancy). Call it once at the end of each frame to convert allocation-triggered minor pauses that would otherwise land mid-frame into scheduled end-of-frame ones, while skipping the collection entirely on frames where the nursery is still mostly empty.
threshold is optional and defaults to 0.6 (overridable process-wide via the SIGIL_GC_FRAME_HINT_THRESHOLD environment variable). Returns #t if a minor collection ran, #f otherwise.
(define (frame)
(update!)
(render!)
(gc-frame-hint!)) ; collect now if the nursery is >60% full
;; Or with a custom threshold:
(gc-frame-hint! 0.75)Always #f on the marksweep GC backend (no young generation); such programs should use gc-step! instead.
gc-idle-hint!procedureIdle-time collection hint for long-running servers.
Call between requests or on an idle timer to move collection work off the request path. It picks the most useful idle work:
- finishes an in-progress major collection, if one is running;
- otherwise starts a full major if the heap has grown past a soft fraction of the next-GC threshold (default
0.8, overridable viaSIGIL_GC_IDLE_MAJOR_FRACTION), so the expensive pause happens while idle rather than mid-request; - otherwise runs a cheap minor collection if there is young garbage;
- otherwise does nothing.
Returns a symbol naming what ran: major, minor, or none.
(define (serve-loop)
(handle-next-request!)
(gc-idle-hint!) ; reclaim between requests
(serve-loop))Pair with SIGIL_GC_HEAP_GROW_FACTOR (e.g. 1.3) for a lower steady-state heap ceiling and SIGIL_GC_MALLOC_TRIM=1 (glibc) to return freed pages to the OS after each major. On the marksweep backend only major/none outcomes occur.
gc-step!procedurePerform incremental GC work.
Processes up to the specified number of objects, then returns. Use in game loops to spread GC work across frames and avoid pauses.
;; In your game loop:
(gc-step! 100) ; Process up to 100 objects per framegc-allocated-bytesprocedureGet the current heap size in bytes.
Returns the total memory currently allocated by the garbage collector. Useful for monitoring memory usage and detecting leaks.
(gc-allocated-bytes)
; => 1234567
;; Monitor allocation in a loop
(let ((before (gc-allocated-bytes)))
(do-work)
(- (gc-allocated-bytes) before))
; => 5000 ; bytes allocated during do-workgc-young-occupancyprocedureGet young-generation occupancy as a fraction of the minor-GC trigger threshold.
Returns a real number: 0.0 right after a collection, rising toward 1.0 as allocations accumulate (a minor collection triggers automatically when it would exceed 1.0). Cheap enough to poll every frame — it performs no allocation. Always 0.0 on the marksweep GC backend.
(gc-young-occupancy)
; => 0.37gc-phaseprocedureGet the current GC phase.
Returns a symbol indicating what the collector is currently doing:
idle— not collecting, normal executionmarking— tracing live objectssweeping— reclaiming dead objects
(gc-phase)
; => idle
(gc-step! 10)
(gc-phase)
; => marking ; or sweeping, depending on progressgc-thresholdprocedureGet the allocation threshold that triggers the next GC cycle.
When gc-allocated-bytes exceeds this value, a collection begins. The threshold grows automatically as your program uses more memory.
(gc-threshold)
; => 2000000
;; Check how close to triggering GC
(- (gc-threshold) (gc-allocated-bytes))
; => 765433 ; bytes until next GCgc-statsprocedureGet detailed GC statistics as an association list.
Fields common to both GC backends:
backend—generationalormarksweep. Check this FIRST when diffing stats: the marksweep backend reports none of the generational counters below.bytes-allocated— current GC-tracked heap sizerss-bytes— process resident set size (0 on non-Linux); compare againstbytes-allocatedto observe fragmentation / allocator drift in long-running processesallocations,collections,next-gc
Generational-backend fields include:
minor-collections/major-collectionsandminor-time-ns/major-time-nsminor-max-pause-ns/major-max-pause-ns— worst pause seenminor-pause-hist/major-pause-hist— pause histograms as lists of counts; bucket upper bounds inpause-hist-bounds-ns(the final bucket is the overflow). The counts in each histogram sum to the corresponding collection counter.young-bytes,young-objects-count,tenured-objects-countyoung-threshold-bytes(+ initial/max/growths), remembered-set and write-barrier counters, reclaimed/promoted byte counters
;; Extract a specific stat
(cdr (assq 'bytes-allocated (gc-stats)))
; => 1234567
;; Fragmentation ratio for a daemon health check
(let ((stats (gc-stats)))
(/ (cdr (assq 'rss-bytes stats))
(max 1 (cdr (assq 'bytes-allocated stats)))))