GC finalizer does not run when object contains reference to itself

114 views
Skip to first unread message

Elemer Pixard

unread,
May 25, 2016, 7:19:50 PM5/25/16
to golang-nuts
Hi,

In my application I use finalizers to release C resources 
acquired by Go objects when these objects
are garbage collected. I noticed that some objects were not
being garbage collected (finalizer was not running) when
they reference themselves (GC cycle ?)

also listed below, reproduces the problem.
The finalizers do not run If the subscription to
the internal dispatcher receiver
is NOT commented out, otherwise they run ok.

Is this correct behavior ?
Will these object ever be garbage collected ?
Any suggestions to  solve this problem ?

Regards.


package main

import (
"fmt"
"runtime"
"time"
)

type Dispatcher struct {
ID        int
callbacks []func()
}

func (d *Dispatcher) Subscribe(cb func()) {

d.callbacks = append(d.callbacks, cb)
}

func (d *Dispatcher) Dispatch() {

for _, cb := range d.callbacks {
cb()
}
}

func (d *Dispatcher) callback() {

fmt.Printf("Dispatcher:%d callback\n", d.ID)
}

func callback() {

fmt.Printf("Stand alone callback\n")
}

func main() {

// List of dispatchers
var disps []*Dispatcher

// Creates some dispatchers
for i := 1; i <= 5; i++ {
d := &Dispatcher{ID: i}
runtime.SetFinalizer(d, func(d *Dispatcher) {
fmt.Printf("Dispatcher:%d finalizer\n", d.ID)
})
// Subscribe to external function
d.Subscribe(callback)
// Subscribe to internal method.
// It will prevent finalizer to run (??)
//d.Subscribe(d.callback)
disps = append(disps, d)
}

// Test dispatch
for _, d := range disps {
d.Dispatch()
}

// Remove references to all dispatchers and force GC
// Finalizers should be executed.
disps = nil
runtime.GC()
// Give some time
time.Sleep(2)
}


Ian Lance Taylor

unread,
May 25, 2016, 7:51:17 PM5/25/16
to Elemer Pixard, golang-nuts
On Wed, May 25, 2016 at 3:24 PM, Elemer Pixard <elp...@gmail.com> wrote:
>
> In my application I use finalizers to release C resources
> acquired by Go objects when these objects
> are garbage collected. I noticed that some objects were not
> being garbage collected (finalizer was not running) when
> they reference themselves (GC cycle ?)
>
> The program: https://play.golang.org/p/i2-rEutbWZ
> also listed below, reproduces the problem.
> The finalizers do not run If the subscription to
> the internal dispatcher receiver
> is NOT commented out, otherwise they run ok.
>
> Is this correct behavior ?
> Will these object ever be garbage collected ?
> Any suggestions to solve this problem ?

The docs for SetFinalizer explain that cyclic structures are not
guaranteed to be finalized. This is not ideal but it is working as
expected.

In your example you should separate the Go struct that holds a C
pointer into a separate type that has just a single field. That
should avoid the cycle.

Ian

keith....@gmail.com

unread,
May 25, 2016, 8:52:24 PM5/25/16
to golang-nuts, elp...@gmail.com
It gets even more interesting.  Self-pointers are ok (the object is still finalizeable), but a self-loop with another object is not.
Changing false to true in the code below changes the finalizer behavior.

package main

import (
"fmt"
"runtime"
"time"
)

type Dispatcher struct {
link *Dispatcher
}

func main() {
for i := 1; i <= 5; i++ {
d := &Dispatcher{}
runtime.SetFinalizer(d, func(d *Dispatcher) {
fmt.Println("Dispatcher finalizer")
})
if false {
d.link = d
} else {
e := &Dispatcher{}
e.link = d
d.link = e
}
}
runtime.GC()
time.Sleep(1000000)
Reply all
Reply to author
Forward
0 new messages