Top Banner
www.cloudflare.com Understanding Go Memory September 11, 2013 John Graham-Cumming
22

Go memory

Jan 15, 2015

Download

Technology

jgrahamc

A talk I gave at the London Go User Group meeting on September 11, 2013 about recycling Go memory.
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Go memory

www.cloudflare.com!

Understanding Go Memory September 11, 2013

John Graham-Cumming

Page 2: Go memory

www.cloudflare.com!

Allocation Primitives •  new(T)!

•  Allocates memory for item with type T!•  Zeroes it��

!•  make(T)!

•  Allocates memory for item with type T!•  Initializes it •  Needed for channels, maps and slices

• Memory comes from internal heap

ret = runtime·mallocgc(typ->size, flag, 1, 1);!

zeroed

Page 3: Go memory

www.cloudflare.com!

Two memory freeing processes • Garbage collection

•  Determines which blocks of memory are no longer used •  Marks areas of heap so they can be reused by your program

• Scavenging •  Determines when parts of the heap are idle for a long time •  Returns memory to the operation system

Page 4: Go memory

www.cloudflare.com!

Garbage collection • Controlled by the GOGC environment variable

•  And by debug.SetGCPercentage()!

•  Default is same as GOGC=100!•  Can set GOGC=off or debug.SetGCPercentage(-1) (no

garbage collection at all)

// Initialized from $GOGC. GOGC=off means no gc.!//!// Next gc is after we've allocated an extra amount of!// memory proportional to the amount already in use.!// If gcpercent=100 and we're using 4M, we'll gc again!// when we get to 8M. This keeps the gc cost in linear!// proportion to the allocation cost. Adjusting gcpercent!// just changes the linear constant (and also the amount of!// extra memory used).!

Page 5: Go memory

www.cloudflare.com!

Scavenging • Runs once per minute

• Can also force return of all unused memory by calling debug.FreeOSMemory()!

// If we go two minutes without a garbage collection, !// force one to run.!forcegc = 2*60*1e9;!!// If a span goes unused for 5 minutes after a garbage!// collection, we hand it back to the operating system.!limit = 5*60*1e9;!

Page 6: Go memory

www.cloudflare.com!

Memory Statistics • Read with runtime.ReadMemStats(&m) !

•  The MemStats struct has tons of members • Useful ones for looking at heap

•  HeapInuse - # bytes in the heap allocated to things •  HeapIdle - # bytes in heap waiting to be used •  HeapSys - # bytes obtained from OS •  HeapReleased - # bytes released to OS

Page 7: Go memory

www.cloudflare.com!

Test garbage making program func makeBuffer() []byte { ! return make([]byte, rand.Intn(5000000)+5000000) !}!!func main() { ! pool := make([][]byte, 20)!! makes := 0 ! for { ! b := makeBuffer() makes += 1!! i := rand.Intn(len(pool))! pool[i] = b!! time.Sleep(time.Second)! }!}!

Page 8: Go memory

www.cloudflare.com!

What happens

Page 9: Go memory

www.cloudflare.com!

debug.FreeOSMemory()!

Page 10: Go memory

www.cloudflare.com!

Use a buffered channel func main() {! pool := make([][]byte, 20)! idle:= make(chan []byte, 5)!! makes := 0! for {! var b []byte! select {! case b = <-idle:! default:! makes += 1! b = makeBuffer()! }!! !

i := rand.Intn(len(pool))! if pool[i] != nil {! select {! case idle<- pool[i]:! pool[i] = nil! default:! }! }!! pool[i] = b!! time.Sleep(time.Second)! }!}

Page 11: Go memory

www.cloudflare.com!

select for non-blocking receive

idle:= make(chan []byte, 5)!!select {!case b = <-idle: !default:! makes += 1! b = makeBuffer()!}!

Try to get from the idle queue

Idle queue empty? Make a

new buffer

A buffered channel makes a

simple queue

Page 12: Go memory

www.cloudflare.com!

select for non-blocking send

idle:= make(chan []byte, 5)!!select {!case buffer <- pool[i]:! pool[i] = nil!!default:!}!

A buffered channel makes a

simple queue

Try to return buffer to the idle queue

Idle queue full? GC will have to deal with the

buffer

Page 13: Go memory

www.cloudflare.com!

What happens

Page 14: Go memory

www.cloudflare.com!

More realistic: 20 goroutines func main() {! pool := make([][]byte, 200)!! for i := 0; i < 10; i++ {! go func(offset int) {! for {! b := makeBuffer()! j := offset+rand.Intn(20)! pool[j] = b!! time.Sleep(time.Millisecond * time.Duration(rand.Intn(1000))! }! }(i*20)! }!}!

Page 15: Go memory

www.cloudflare.com!

What happens

Page 16: Go memory

www.cloudflare.com!

Shared across goroutines func main() {! buffer := make(chan []byte, 5)!! pool := make([][]byte, 200)! for i := 0; i < 10; i++ {! go func(offset int) {! for {! var b []byte! select {! case b = <-buffer:! default: b = makeBuffer()! }! j := offset+rand.Intn(20)! if pool[j] != nil {! select {! case buffer <- pool[j]: pool[j] = nil! default:! }! }! pool[j] = b! time.Sleep(time.Millisecond * time.Duration(rand.Intn(1000))! }! }(i*20)! }!!

Page 17: Go memory

www.cloudflare.com!

What Happens

Page 18: Go memory

www.cloudflare.com!

More realistic example • Alter code to

•  Always try to give back a random buffer from the pool •  50% of the time get a new one

• Should create more garbage

Page 19: Go memory

www.cloudflare.com!

Idle length 5

Page 20: Go memory

www.cloudflare.com!

Idle length 20

Page 21: Go memory

www.cloudflare.com!

Idle length 50

Page 22: Go memory

www.cloudflare.com!

Also •  This works for things other than []byte

•  Can be done with arbitrary types •  Just need some way to reset

•  There’s a proposal to add something like this to the Go package library •  sync.Cache •  Follow TODO