Skip to content

wasm: linear memory declared at 3.3GB, where the same program built with Go declares 29MB #5599

Description

@0pcom

TinyGo 0.41.1, -target wasm, browser.

A wasm module I build declares 3328 MB of linear memory. The same source built with standard Go declares 29 MB — a factor of 115. Reading memory.buffer.byteLength immediately after instantiateStreaming, before go.run, already reports 3328 MB, so this is declared rather than grown, and no Go code has run to allocate it.

It matters beyond the footprint: at 3.3GB, ordinary addresses sit above 2GB, which puts every syscall/js call into #5095 (addresses arriving as signed i32). The terminals in my program stay blank while everything around them draws, one uncaught RangeError per call.

What it is not

  • Not the collector. -gc=precise produces byte-identically 3328 MB. Consistent with the memory never having been allocated by the GC.
  • Not -stack-size. I pass -stack-size 1MB; on a small binary that accounts for about 6MB (2MB without it, 8MB with).
  • Not any one dependency. Built individually as package main importing only that package, with the same flags:
import declared
charmbracelet/glamour (pulls goldmark, chroma) 11 MB
gdamore/tcell/v2 1 MB
rivo/tview 1 MB
gdamore/proxima5 2 MB
xterm-go (a VT emulator) 1 MB
two smaller DOM packages 0 MB
all of them, in one program 3328 MB

So it appears to be emergent from the combination rather than attributable to a package, which is why I have not been able to reduce it further. Happy to bisect against a hypothesis if there is one worth testing — each build takes about 17 minutes, so I would rather aim than sweep.

Measuring it

// in wasm_exec.js constructor
globalThis.__memlog = () => this._inst.exports.memory.buffer.byteLength;

then, after instantiation:

(globalThis.__memlog() / 1048576).toFixed(0) + " MB"   // 3328 MB

Flags: -no-debug -stack-size 1MB -interp-timeout 10m. The stack size is needed because the default overflows in regexp/syntax.Simplify when expanding a bounded repeat ({1,256} in goldmark's linkify pattern) — the overflow does not fault, it corrupts, and regexp then panics at startup on a case it treats as unreachable. The interp timeout is needed because the default three minutes is not enough for goldmark.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions