Skip to content

Auto Free GC

Hugo edited this page Sep 25, 2026 · 2 revisions

Auto-Free GC

CoreTrace auto-free has two layers:

  • compile-time feature enable (--ct-autofree)
  • optional conservative runtime scanning (CT_AUTOFREE_SCAN*)

Implementation: src/runtime/ct_runtime_autofree_scan.cpp, split out of the allocation runtime so the scan is a translation unit of its own. The allocation table it walks lives in ct_runtime_alloc.cpp and is mutex-guarded, so the periodic scan thread and the program's own allocations do not race.

1) Compile-time enable

Auto-free is disabled by default in compile-time runtime config. Enable it when building instrumented binaries:

./build/cc --instrument --ct-alloc --ct-autofree main.c -o app

Two things happen at compile time:

  • An allocation whose result the program provably never uses is rewritten to the *_unreachable entry point (__ct_malloc_unreachable, __ct_new_unreachable, …), which lets the runtime release it at once.
  • An allocation proven unreachable at the end of a frame gets an __ct_autofree* call at function exit (__ct_autofree, __ct_autofree_delete, __ct_autofree_delete_array, __ct_autofree_munmap, __ct_autofree_sbrk).

An address that escapes — stored into a scalar slot that outlives the frame, converted through ptrtoint/inttoptr, selected into a value that leaves the function — is not auto-freed. The fixtures under test/ name each of those shapes (ct_autofree_scalar_slot_escape.c, ct_autofree_ptrtoint_escape.c, ct_autofree_select_escape.c, …).

Tracked allocation kinds include malloc, new, new[], mmap, sbrk and, on Apple targets, Objective-C objects — see Objective-C Support.

2) Runtime scan enable

Without scan variables, auto-free only uses compile-time reachability decisions. To enable conservative root scanning:

CT_AUTOFREE_SCAN=1 ./app

To enable periodic GC thread:

CT_AUTOFREE_SCAN=1 CT_AUTOFREE_SCAN_START=1 ./app

Scan variables

Variable Default Meaning
CT_AUTOFREE_SCAN 0 Enable conservative scan system
CT_AUTOFREE_SCAN_START 0 Start background periodic scan thread
CT_AUTOFREE_SCAN_STACK 1 Scan stack roots
CT_AUTOFREE_SCAN_REGS 1 Scan register roots
CT_AUTOFREE_SCAN_GLOBALS 1 Scan globals (__DATA/image data)
CT_AUTOFREE_SCAN_INTERIOR 1 Count interior pointers as roots
CT_AUTOFREE_SCAN_PTR 1 Run per-pointer check before auto-free
CT_DEBUG_AUTOFREE_SCAN 0 Debug level (0/1/2)

Timing controls

Period precedence:

  1. CT_AUTOFREE_SCAN_PERIOD_NS
  2. CT_AUTOFREE_SCAN_PERIOD_US
  3. CT_AUTOFREE_SCAN_PERIOD_MS
  4. fallback 1000ms when START=1 and no period is set

Budget precedence:

  1. CT_AUTOFREE_SCAN_BUDGET_NS
  2. CT_AUTOFREE_SCAN_BUDGET_US
  3. CT_AUTOFREE_SCAN_BUDGET_MS (default 5.0)

Compatibility interval:

  • CT_AUTOFREE_SCAN_INTERVAL_MS sets a minimum interval for per-pointer scans.

Example profiles

Safer debug profile

CT_AUTOFREE_SCAN=1 \
CT_AUTOFREE_SCAN_START=1 \
CT_AUTOFREE_SCAN_PTR=1 \
CT_AUTOFREE_SCAN_GLOBALS=1 \
CT_AUTOFREE_SCAN_PERIOD_MS=1000 \
CT_AUTOFREE_SCAN_BUDGET_MS=20 \
./app

Lower overhead profile

CT_AUTOFREE_SCAN=1 \
CT_AUTOFREE_SCAN_START=1 \
CT_AUTOFREE_SCAN_GLOBALS=0 \
CT_AUTOFREE_SCAN_PTR=0 \
CT_AUTOFREE_SCAN_PERIOD_MS=5000 \
CT_AUTOFREE_SCAN_BUDGET_MS=5 \
./app

Defaults worth knowing

  • CT_AUTOFREE_SCAN_BUDGET_MS defaults to 5.0; the period has no default until CT_AUTOFREE_SCAN_START=1, which sets it to 1000 ms when no period was given.
  • Within each pair, precedence is NS > US > MS: setting a nanosecond value ignores the millisecond one.

Practical caveats

  • Conservative roots can keep dead allocations alive (false negatives). That is the safe direction: the scan never frees something it merely failed to see a root for.
  • If the budget is exceeded, the scan frees nothing in that pass, rather than freeing the part it managed to prove.
  • Pointer scanning and global scanning can be expensive on large processes; CT_AUTOFREE_SCAN_PTR=0 and CT_AUTOFREE_SCAN_GLOBALS=0 are the two levers.
  • Stale values left on the stack, in registers or in globals can look like live pointers. This is conservative collection, not precise reachability.

Verifying it on your own build

bash test/run_autofree_tests.sh

The auto-free fixtures cover each escape shape and each allocation kind; see Testing and Validation.

Clone this wiki locally