Go

What does this program print? Explain how finalizers behave.

Question 354HardGo 1.22 to 1.25
type Res struct{ name string }

func main() {
    r := &Res{name: "db"}
    runtime.SetFinalizer(r, func(r *Res) { fmt.Println("finalized", r.name) })
    r = nil
    runtime.GC()
    fmt.Println("done")
}

Answer: done is guaranteed. finalized db may or may not print, before or after done. Finalizers run later on a single dedicated goroutine, and the program does not wait for them when it exits. Adding time.Sleep makes it likely to print, but still not guaranteed.

Finalizer gotchas:

  • Never guaranteed to run: not at exit, and not for objects in the tiny allocator or package-level variables. They are not a replacement for Close().
  • They resurrect the object: the finalizer gets the pointer, so freeing it takes at least two GC cycles.
  • Cycles leak: if objects in a reference cycle have finalizers, the cycle is never collected.
  • Only one finalizer per object, and it must be set on the start of an allocation, not an interior pointer.
  • All finalizers share one goroutine, so a slow or blocking finalizer stalls the rest.
  • The object can be finalized while one of its methods is still running. See runtime.KeepAlive.

What the interviewer is looking for: finalizers are a leak-detection safety net (the os.File pattern), never your primary cleanup. Since Go 1.24, prefer runtime.AddCleanup.

More on Memory, GC & Runtime Internals

All 38 Memory, GC & Runtime Internals questions