Class 26: Closure & Go Internal Memory Deep Dive ๐ก
Welcome to Class 26, where we uncover the magic behind closures in Go, escape analysis, and how memory is managed under the hood! ๐ง ๐ฅ
๐งพ The Code
package main
import "fmt"
const a = 10
var p = 100
//Closure
func outer(money int) func() {
age := 30
fmt.Println("Age =", age)
show := func() {
money = money + a + p
fmt.Println(money)
}
return show
}
func call() {
incr1 := outer(100)
incr1() // money = 100 + 10 + 100 = 210
incr1() // money = 210 + 10 + 100 = 320
incr2 := outer(100)
incr2()
incr2()
}
func main() {
call()
}
func init() {
fmt.Println("=== Bank ===")
}
๐ Key Concepts
๐ What is a Closure?
A closure is a function that references variables from outside its own scope. In this case:
show := func() {
money = money + a + p
fmt.Println(money)
}
show
forms a closure by capturing the money
variable defined in outer()
.
๐ง Why is Closure Important?
Closures let you encapsulate logic along with state. This is why incr1()
and incr2()
maintain separate money
values even though they use the same function.
๐งฎ Stack vs Heap
- Stack: Fast memory, used for function calls and local variables.
- Heap: Used when variables need to persist beyond the function call (like in closures!).
Because money
needs to stick around after outer()
returns, escape analysis detects this and allocates money
on the heap.
๐งช What is Escape Analysis?
Escape analysis is the process that the Go compiler uses during the compilation phase to determine whether variables can be safely allocated on the stack or must go to the heap.
- โ If a variable is used only inside a function, it's put on the stack.
- ๐ If a variable is used outside (like in a returned closure), it's moved to the heap.
๐งฑ Memory Segments
Segment | What's Stored |
---|---|
Code Segment | Compiled instructions (functions) |
Data Segment | Global and static variables (a , p ) |
Stack | Local variables (age ) |
Heap | Escaping variables (money ) |
๐ง Visualization
CLI-Style Memory Layout
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Code Segment โ
โ-----------------------------โ
โ main, call, init, outer, โ
โ anonymous show function โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Data Segment โ
โ-----------------------------โ
โ const a = 10 โ
โ var p = 100 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Stack โ
โ-----------------------------โ
โ outer() frame โ
โ age = 30 โ
โ return address โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Heap โ
โ-----------------------------โ
โ money = 100 (for incr1) โ
โ money = 100 (for incr2) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Each closure has its own money
on the heap. Every call to outer(100)
results in a new memory block being allocated.
Garbage Collectorโs Role ๐งน
When the closure is no longer referenced (e.g., incr1
or incr2
goes out of scope), the Garbage Collector detects that the heap memory (e.g., money
) is unreachable. It then safely reclaims that memory so your program doesnโt become a memory hoarder. This is vital for maintaining efficiency, especially when many closures are involved.
GC is triggered automatically and runs concurrently with your program. It uses a combination of mark-and-sweep and concurrent garbage collection techniques to do this efficiently.
๐ง TL;DR
- Closures can capture and remember variable state ๐
- Escape analysis figures out which variables must live on the heap ๐ฆ
- Stack is temporary, heap is persistent (with GC ๐งน)
- Go separates memory into Code, Data, Stack, Heap โ each with its role ๐งฉ
- GC ensures unused heap memory (like old closure data) is recycled โป๏ธ