#67
feat: add vm.getAllocatedSize() for constant-time memory sampling
Problem
vm.getMemoryUsage() is the only way to read the runtime's memory use, and it's backed by JS_ComputeMemoryUsage, which walks every object, string and shape on the heap. That makes sampling memory on every host call O(heap): a host that tracks a guest's memory as it runs ends up spending most of its CPU in the walk once the guest holds a sizeable dataset.
Measured with a local benchmark script, not included in this PR (Apple M4 Pro, bun, best of 5 rounds):
| Heap | getMemoryUsage() | getAllocatedSize() |
|---|---|---|
| Empty (0.2 MB) | 2.1 µs/call | 0.01 µs/call |
| ~100 MB (450,000 records, 900k objects) | 5,219 µs/call | 0.01 µs/call |
10,000 samples on the 100 MB heap: ~52 s with getMemoryUsage() (extrapolated from 100 calls), ~0.1 ms with getAllocatedSize().
Change
- New C export
qjs_get_allocated_size, which returns the byte counter that the runtime's malloc functions inc/interface.calready maintain on every allocation and free (qjs_mem_used, added in #33). - New
vm.getAllocatedSize(), which reads it in O(1). getMemoryUsage()gains anallocatedSizefield holding the same value, so the full breakdown shows it too.- README note under "Memory limits" on when to use which; changeset (minor).
Tests
test/allocated-size.test.ts:
- matches
getMemoryUsage().allocatedSizeand stays close tomallocSizeon a fresh VM, after allocating a large array, and after freeing it and running GC - grows by its delta in step with
mallocSize, and counts ArrayBuffer backing stores - reaches, but never exceeds,
memoryLimitwhen retained buffers trigger OOM - after
QuickJS.restore()equals the value before the snapshot, and keeps tracking allocations and frees
test/memory-usage.test.ts checks the new field. Full suite: 42 files, 2082 tests passing against a rebuilt quickjs.wasm (wasi-sdk 32).
@ethndotsh is attempting to deploy a commit to the Vercel Labs Team on Vercel.
A member of the Team first needs to authorize it.