Memory management
Wasm memory grows but never shrinks back to the OS, so the goal is simple: don't let working set build up. Four habits cover almost everything.
Close datasets when you're done
const ds = await Gdal.open("/opfs/in.gpkg");
const out = await ds.vectorTranslate("/opfs/out.geojson", opts);
await out.close(); // flushes and frees the output
await ds.close(); // frees the input
Every open dataset holds GDAL-side buffers. close() flushes writes and releases them. Skipping it is the most common source of "memory only ever goes up".
Clean the virtual FS between jobs
Converted outputs stay in the virtual filesystem until you remove them (that's by design: you read them back after the call). In long-running apps, delete what you've consumed: Gdal.unlink(path), Gdal.unlinkBatch(paths), Gdal.rmdirRecursive(dir). On the v2 line the same need produced clearFS() (community PR #109). Details in the VFS guide.
Tune GDAL's block cache
await Gdal.setCacheMax(32 * 1024 * 1024); // default is larger; bytes
console.log(await Gdal.getCacheUsed());
Raster I/O goes through a block cache. Big cache = faster repeated reads, more resident memory. On memory-tight targets lower it before heavy raster work.
Browser limits and the mobile escape hatch
Mobile browsers cap per-tab memory aggressively (the iOS Safari ceiling reported in issue #96). You can stretch the budget (st build, smaller cache, OPFS-streamed inputs instead of in-memory copies, one dataset at a time), but physics wins eventually.
v3's structural answer: on iOS and Android, run gdal3.js as a native module via React Native. No wasm sandbox, no tab limit. GDAL gets app-level memory. Same JavaScript on top.