← back to index
wasthon — tp_dealloc A/B proof
One page, two phases on the same module, identical workload — a tight loop of
_pickle.dumps(obj). The C dumps() creates a
Pickler (a refcounted wasthon instance) and Py_DECREFs
it internally; that zero-refcount event is the only place
tp_dealloc can fire (a Brython del reaches no
FinalizationRegistry).
Phase A: runtime.noFree = false — refcount→0
dispatches tp_dealloc → instance freed.
Phase B: runtime.noFree = true — refcount→0
does nothing, reproducing the bridge before tp_dealloc existed.
The deterministic, GC-independent signal is refcounts.size
(count of live refcounted instances). Phase A ⇒ flat (every Pickler reclaimed);
Phase B ⇒ climbs by one per dumps() (instances pile up). Tune with
?iters=20000.
Caveat: the WASM-heap column grows in both phases — pickling
also leaks JS-side sentinel handles (a separate problem tp_dealloc
structurally can't touch), and HEAPU8.length only grows in coarse
steps. That byte axis can't isolate tp_dealloc; refcounts.size
is the signal that does.
Loading…
Phase A — tp_dealloc enabled (noFree=false)
| iter | refcounts.size | Δ refcounts |
handles.size | WASM (MB) | Δ WASM (kB) |
Phase B — tp_dealloc disabled (noFree=true)
| iter | refcounts.size | Δ refcounts |
handles.size | WASM (MB) | Δ WASM (kB) |