# shard.yml
dependencies:
gcry:
github: sdogruyol/gcry
# app.cr
{% if flag?(:gc_none) %}
require "gcry"
{% end %}crystal build -Dgc_none app.crUnder -Dgc_none, require "gcry" reopens Crystal’s GC module. Everyday code keeps using String / Array — no separate malloc API for app objects.
| API | Role |
|---|---|
Gcry::VERSION |
Shard version string |
Gcry.default_heap |
Process / default Heap |
Gcry.malloc / malloc_atomic / realloc / free |
Library-heap helpers (tests) |
Gcry.usable_size(ptr) |
Usable size of the live block containing ptr (base or interior), at least the requested size (Boehm GC_size); 0 when no live gcry block contains it |
Gcry.collect / minor_collect / collect_a_little |
Manual collection |
Gcry.pause_stats |
STW pause ring (last / p50 / p99 / max / count) |
Gcry.metrics |
Extended counters (collections, RSS-ish bytes, blacklist, …) |
Gcry.prometheus_text |
Prometheus exposition format |
Gcry::Observability.json_stats |
JSON snapshot for /gc-stats |
Gcry.dump_heap(io) |
Live-object NDJSON dump (debug; large heaps may take seconds) |
Gcry.dump_heap_addresses / heap_dump_gone / heap_dump_new |
Address-set dump + leak diff helpers |
Gcry::Trace |
NDJSON event log (GCRY_TRACE=1); writes via LibC.write (no JSON/OpenSSL in GC path) |
Conservative mark–sweep allocator used as the process GC or as a private heap under Boehm in specs.
Notable knobs (also via GCRY_* env on process GC): gc_threshold, nursery_enabled, incremental_auto, release_empty_chunks, type_id_gate, blacklist_enabled, tlab_enabled, stop_the_world.
The nursery is a young-object space (size-class chunks tagged NURSERY). Allocations go there when nursery_enabled is true. A minor collection walks roots + old→young edges (via page-dirty barrier on Linux, full old scan on Darwin) and promotes survivors to old-space.
Process GC defaults:
- Linux + Darwin: nursery disabled (soft-dirty false negatives on HTTP made default-on unsafe). Opt in with
GCRY_NURSERY=1(adaptive threshold still applies when enabled). - Soft-dirty / mprotect barrier backends remain Linux-only; Darwin nursery uses full old→young scan if enabled.
When adaptive_nursery is true (default), the threshold adjusts after each minor based on the moving-average survival rate (last 10 minors):
| Survival rate | Action |
|---|---|
| > 50% target | Threshold grows 25% (collect less often) |
| < 25% | Threshold shrinks 25% (collect sooner, limit survivor pressure) |
Clamped to [64 KiB, 8 MiB]. Disable with GCRY_DISABLE_ADAPTIVE_NURSERY=1 or set heap.adaptive_nursery = false.
/gc-stats exposes nursery_survival_bytes, nursery_alloc_before_minor, nursery_survival_rate_pct, adaptive_nursery. Prometheus exposes gcry_nursery_* gauges.
See README.md Tuning table and HARDENING.md.
{% if flag?(:gc_none) %}
get "/metrics" do |env|
env.response.content_type = "text/plain; version=0.0.4"
Gcry.prometheus_text
end
get "/gc-stats" do |env|
env.response.content_type = "application/json"
Gcry::Observability.json_stats
end
{% end %}Kemal bench and acikturkiye dogfood the same helpers: GET /gc-stats → Gcry::Observability.json_stats, GET /metrics → Gcry.prometheus_text (under -Dgc_none).