Go

What does runtime.KeepAlive do and when is it required?

Question 357HardGo 1.22 to 1.25

The GC decides liveness from the point of last use, not from lexical scope. An object can therefore become unreachable, and its finalizer or cleanup can run, in the middle of a function that is still using a resource derived from it, such as an fd or C pointer extracted from its fields. runtime.KeepAlive(x) marks x as used up to that point. It compiles to almost nothing.

type FD struct{ fd int }

func OpenFD(path string) (*FD, error) {
    fd, err := syscall.Open(path, syscall.O_RDONLY, 0)
    if err != nil {
        return nil, err
    }
    f := &FD{fd: fd}
    runtime.AddCleanup(f, func(fd int) { syscall.Close(fd) }, fd)
    return f, nil
}

func (f *FD) Read(p []byte) (int, error) {
    fd := f.fd
    // After this line f is no longer used, so without KeepAlive
    // the GC could collect f and close fd DURING the syscall.
    n, err := syscall.Read(fd, p)
    runtime.KeepAlive(f)
    return n, err
}

You need it when all three hold: (1) the object has a finalizer or cleanup that releases a resource, (2) you extracted a raw handle or uintptr from it, and (3) you keep using that handle after your last Go-level use of the object. It is also common in cgo code that passes C pointers owned by a Go wrapper.

What the interviewer is looking for: you know that scope does not equal liveness in Go, and that this bug is real. os.File and net use this pattern internally. The syscall calls above assume a Unix-like platform.

More on Memory, GC & Runtime Internals

All 38 Memory, GC & Runtime Internals questions