In your simple example, the defer
statement is not required. The file is closed at the time the function scope is exited.
If, however, you return the variable f
or save it in a global, its reference counter is incremented and thus it doesn't get deleted.
Here is an example to prove that the finalizer gets called immediately (it's garbage collected immediately because the reference counter reaches zero) in the reverse order they were initialized:
package main
import (
"fmt"
"runtime"
"time"
)
type Foo struct {
name string
num int
}
func finalizer(f *Foo) {
fmt.Println("a finalizer has run for ", f.name, f.num)
}
var counter int
func MakeFoo(name string) (a_foo *Foo) {
a_foo = &Foo{name, counter}
counter++
runtime.SetFinalizer(a_foo, finalizer)
return
}
func Bar() {
f1 := MakeFoo("one")
f2 := MakeFoo("two")
fmt.Println("f1 is: ", f1.name)
fmt.Println("f2 is: ", f2.name)
}
func main() {
for i := 0; i < 3; i++ {
Bar()
time.Sleep(time.Second)
runtime.GC()
}
fmt.Println("done.")
}
The fact is that you have no real control over whether the finalizer gets called. But if you make sure to not copy the local variable anywhere, then it happens immediately.
Actually, an interesting fact is that if you use defer, then you create a reference to the object (you need to have a reference to be able to call the Close()
at the time you quit your function).
This is a very important point since, if you have a loop, you'd actually make it much worse:
...
for i:=0; i<10; i++ {
f := os.Open(...)
defer f.Close()
...
} // without the defer, the file is closed here, as expected
} // <- defer is called here, so you will open 10 files
// and keep all 10 open until this line
So you have to be pretty careful about the defer
. There are situations where it doesn't work.