8

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)?

Riking
  • 2,389
  • 1
  • 24
  • 36
  • 5
    Looks like the `funcval` struct in runtime/runtime2.go would be of use. – Riking Jul 24 '18 at 02:13
  • 3
    The other question is about _how_ the closures work in practice, but what you’re asking is for implementation details from an undefined version of a particular Go compiler. While it may be interesting, I don’t know if SO is the place to ask. – JimB Jul 24 '18 at 03:13
  • The golang FAQ might help: https://golang.org/doc/faq#stack_or_heap which is similar for all if is it functions/closures etc – Tinkaal Gogoi Jun 21 '21 at 10:45

2 Answers2

9

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.

peterSO
  • 158,998
  • 31
  • 281
  • 276
2

This blog post answers the question for the current version of the Go compiler:

https://philpearl.github.io/post/functionpointers/

In Go as it is presently…

  • Function variables are pointers
  • They point to small structs
  • Those structs contain either
    • just the pointer to the function code for simple functions
    • or to pointers to autogenerated wrapper functions and receivers &/or function parameters in the case of method calls and closures.
Riking
  • 2,389
  • 1
  • 24
  • 36