Skip to content

Memory leak involving instances and WrapFunc (due to cycle?) #57

@tsandall

Description

@tsandall

Hello!

I'm running into a memory leak in v0.22.0 after picking up the fix for #42. Here's a simple program that reproduces it:

package main

import (
	"runtime"

	"github.com/bytecodealliance/wasmtime-go"
)

type X struct {
	instance *wasmtime.Instance
}

func (x *X) F() {

}

func main() {

	bs, err := wasmtime.Wat2Wasm(`
	(module
	  (import "" "hello" (func $hello))
	  (func (export "run")
		(call $hello))
	)
  `)
	check(err)

	for {

		x := &X{}

		store := wasmtime.NewStore(wasmtime.NewEngine())
		imports := []*wasmtime.Extern{wasmtime.WrapFunc(store, x.F).AsExtern()}
		module, err := wasmtime.NewModule(store.Engine, bs)
		check(err)

		instance, err := wasmtime.NewInstance(store, module, imports)
		check(err)

		x.instance = instance

		runtime.GC()
	}
}

func check(e error) {
	if e != nil {
		panic(e)
	}
}

This is a extension of the example from #42. The important difference is that the function passed to WrapFunc closes over the Instance because it's a member function of x. I think this creates a cycle that ultimately prevents the GC from calling the Instance finalizer.

I added some print statements into instance.go and func.go and confirmed that:

  • The instance finalizer is never called
  • The goFinalizeWrap function is never called and gWrapMap grows indefinitely

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions