-
Notifications
You must be signed in to change notification settings - Fork 378
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WeakMap memory leak #199
Comments
new WeakMap().set(x = {}, {}) There is a circular reference:
So the finalizer on |
You are right. I tried to make the implementation as unobtrusive as possible because many people don't use weak collections. Unfortunately, I failed 😞 Your particular case could be fixed by introducing a wrapper around Runtime, so that the current runtime becomes unexported: type Runtime struct {
*runtime
} then setting a finalizer on *Runtime which clears the globalObject. However, this will not work if an object is used as its own value in a weak collection (i.e. if you do The only way I can see to solve this would be doing the same wrapper trick on What I could do instead is detect this and remove the finalizer, so that at least the object is garbage-collected when the weakmap itself becomes unreferenced. Unless there is a better suggestion, I'll do it. |
In fact, it won't work in a more generic case: if the value has a reference to the key, i.e. |
I actually encountered this problem in core-js,
Yes, I'm also thinking over if there is a better solution which can also cover this case. |
Giving it more thought, I don't think adding a finalizer to Runtime is a good idea. Imagine there is a custom struct that refers to Runtime and this struct is put into the globalObject. Suddenly this makes the whole Runtime not garbage-collectable. And even the trick with *Object won't solve all cases. In fact I believe there is no perfect solution within the current Go functionality. There is simply no way to guarantee there are no circular references (for example the key may be the map itself). I know Go authors don't want to make improvements with finalizers because they believe they are "overused". Maybe it's worth making a case to them? |
I agree that Go should improve finalizers, however, not likely in the short term. Currently, maybe we could implement a "lazy gc" on In that case, a type weakMap struct {
// need to synchronise access to the data map because it may be accessed
// from the finalizer goroutine
sync.Mutex
data map[uint64]Value
removals *removals
}
// TODO: locks.
type removals struct{
keys []uint64
}
func (r *removals) removeId(id uint64) {
r.keys = append(r.keys, id)
}
func (vm *weakMap) gc() {
for _, id := range vm.removals.keys {
vm.removeId(id)
}
vm.removals.keys = nil
}
func (wm *weakMap) set(key *Object, value Value) {
wm.gc()
...
refs.add(wm.removals)
} |
I was thinking along the same line. This approach seems the most reasonable. Hopefully the change will fix the problem and will not create any unwanted side effects. |
good job! |
$ go test weakmap_test.go -memprofile=/tmp/heap
There are still tons of inuse_objects:
Still have no idea why (it seems to be related with runtime.SetFinalizer).
The text was updated successfully, but these errors were encountered: