Skip to content

WASM/WASIp2: GC Frees do not free #5249

@t4chib4ne

Description

@t4chib4ne

Hi,

I was trying run a webserver in WASM and build this for testing. Even while idle the allocated memory grew. Either my implementation leaks, TinyGo GC does not free or wasmtime does not free.

I already saw #4704 and wrote a similar test which is easier to run with wasmtime:

package main

// Building
// 	Big Go: go build -o big-leak
// 	TinyGo: tinygo build -o tiny-leak
// 	WASM:   tinygo build -target wasip2 -o leak.wasm
// Running
// 	Big Go: ./big-leak
// 	TinyGo: ./tiny-leak
// 	WASM:   wasmtime leak.wasm

import (
	"fmt"
	"math/rand"
	"runtime"
	"time"
)

const MiB = 1024 * 1024

func main() {
	ticker := time.NewTicker(250 * time.Millisecond)
	defer ticker.Stop()

	var stats runtime.MemStats

	for range ticker.C {
		b := alloc()
		fmt.Print(b[rand.Intn(len(b))])

		runtime.ReadMemStats(&stats)
		fmt.Printf(" HeapAlloc=%dM HeapReleased=%dM Frees=%d\n", stats.HeapAlloc/MiB, stats.HeapReleased/MiB, stats.Frees)
	}
}

//go:noinline
func alloc() []byte {
	return make([]byte, MiB)
}

On my system and running via wasmtime the memory does not even top out. Strangely the runtime reports the GC is freeing stuff but the memory does not seem to be released.

In my testing I saw:

  • for go version go1.25.5 X:nodwarf5 linux/amd64: HeapAlloc tops out at 4MiB
  • for tinygo version 0.40.1 linux/amd64 (using go version go1.25.5 X:nodwarf5 and LLVM version 20.1.1): HeapAlloc stays at 0MiB and in top memory never exceeds 3MiB. Guess TinyGo was able to optimize the Heap allocation away?
  • for TinyGo and wasmtime 41.0.0: A bunch of Frees but only a growing HeapAlloc

I'll gladly swap out some TinyGo options for further testing because I am not that familiar with runtime, compilation and GC inner workings.

Thank you.

Metadata

Metadata

Assignees

Labels

wasmWebAssembly

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions