Search code examples
goclosures

How are Go closures layed out in memory?


For an explanation of closures in general, see How do JavaScript closures work?

How exactly are Go closures laid out in memory?

Take, for example, the following function:

type M int

func (m *M) Adder(amount int) func() {
    return func() {
        *m = *m + amount
    }
}

When our code calls a := m.Adder(), how much memory is allocated on the heap and what does it look like? How much memory does the returned func() value take up (wherever in memory it ends up being)?


Solution

  • The Go Programming Language Specification

    Function literals

    A function literal represents an anonymous function.

    FunctionLit = "func" Signature FunctionBody .

    func(a, b int, z float64) bool { return a*b < int(z) }

    A function literal can be assigned to a variable or invoked directly.

    f := func(x, y int) int { return x + y }
    func(ch chan int) { ch <- ACK }(replyChan)
    

    Function literals are closures: they may refer to variables defined in a surrounding function. Those variables are then shared between the surrounding function and the function literal, and they survive as long as they are accessible.


    Closures may refer to variables defined in a surrounding function. Those variables are then shared between the surrounding function and the function literal, and they survive as long as they are accessible.

    Variables that survive a function call are put on the heap. In Go, closures are really that simple.


    For example,

    func closure() func() *byte {
        var b [4 * 1024]byte
        return func() *byte {
            return &b[0]
        }
    }
    

    A closure() call is two heap allocations, one for 16 (= 8 + 8 on amd64) bytes

    struct { F uintptr; b *[4096]byte }
    

    and one for 4096 bytes

    [4096]byte
    

    for a total of 4112 bytes.