Marco Peereboom
unread,Aug 28, 2013, 12:27:07 PM8/28/13Sign in to reply to author
Sign in to forward
You do not have permission to delete messages in this group
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
to golan...@googlegroups.com
Executive overview:
Repeatedly creating and destroying maps leaks memory over time. Depending on
the OS settings either a whole lot of memory will be occupied or the program
eventually crashes.
The test program is a little longish but it is boiled down from a real program
we are developing. That program creates large maps of sha256, stuffs them in a
database and then tosses the map. Adding a call to runtime.GC() prolongs the
runtime however in the end the results are always the same.
Everyone I have shown this program so far has gone through the 5 phases of
denial. So before you dismiss it right away I invite you to play with the test
program. And, yes, I am fully aware of the nits that are in it and debating
those isn't my goal :) Unless I am missing something the result is that a
little bit of memory is leaked every couple of runs and eventually bad things
happen.
These are the go versions I have tested this program with:
go version devel +8bf13ae02c8e Mon Aug 19 16:22:33 2013 +1000 openbsd/amd64
go version devel +1d0a0a267088 Wed Aug 14 22:18:49 2013 +0400 linux/386
go version devel +8bf13ae02c8e Mon Aug 19 16:22:33 2013 +1000 darwin/amd64
go version go1.1.2 windows/386
go version go1.1.2 windows/amd64
-----------------------------------------------------------------------
package main
import (
"crypto/sha256"
"encoding/binary"
"fmt"
"runtime"
"runtime/debug"
"strconv"
"sync"
)
var done sync.WaitGroup
type MemTest struct {
postfix string
maxSize int64
mymap map[string][]byte
}
func NewMemTest(postfix string) (*MemTest, error) {
mt := MemTest{}
mt.postfix = postfix
mt.maxSize = 16 * 1024 * 1024
return &mt, nil
}
func (mt *MemTest) start() {
var i, keySize, valueSize int64
done.Add(1)
defer done.Done()
mt.mymap = make(map[string][]byte)
keySize = sha256.Size * 2 // ascii
valueSize = 16
elements := mt.maxSize / (keySize + valueSize) // this really is off
for i = 0; i < elements; i++ {
digest := sha256.New()
sha := make([]byte, 16)
binary.PutVarint(sha, int64(i))
digest.Write(sha)
d := digest.Sum(nil)
ds := fmt.Sprintf("%x", d)
buf := make([]byte, valueSize)
mt.mymap[ds] = buf
}
}
func (mt *MemTest) Delete() {
mt.mymap = nil
}
func getusage() (uint64, uint64) {
var m runtime.MemStats
runtime.ReadMemStats(&m)
return m.HeapAlloc, m.HeapObjects
}
func main() {
var delta int64
trygc := false
tryfree := false
runtime.GOMAXPROCS(runtime.NumCPU())
prev, _ := getusage()
fmt.Printf("Start Of Day: %v\n", prev)
memtests := make(map[string]*MemTest, 16)
for {
for x := 0; x < 8; x++ {
mt, err := NewMemTest(strconv.Itoa(x))
if err != nil {
fmt.Printf("could not create a new mem test")
continue
}
memtests[strconv.Itoa(x)] = mt
go mt.start()
}
done.Wait()
for k, mt := range memtests {
mt.Delete()
delete(memtests, k)
}
if tryfree == false && trygc == true {
runtime.GC()
}
if tryfree {
debug.FreeOSMemory()
}
now, objects := getusage()
delta = int64(now - prev)
fmt.Printf("Usage: %v delta %v objects %v\n", now, delta, objects)
prev = now
}
}
-----------------------------------------------------------------------
Example output:
Start Of Day: 175624
Usage: 273175944 delta 273000320 objects 3556907
Usage: 371666496 delta 98490552 objects 6379833
Usage: 404890128 delta 33223632 objects 7352573
Usage: 400073592 delta -4816536 objects 7246046
Usage: 406229672 delta 6156080 objects 7424067
Usage: 336162032 delta -70067640 objects 5226654
Usage: 339698072 delta 3536040 objects 5490347
Usage: 398100744 delta 58402672 objects 7006394
Usage: 331054968 delta -67045776 objects 5235921
Usage: 289647232 delta -41407736 objects 4033146
Usage: 423024248 delta 133377016 objects 7897517
Usage: 341334560 delta -81689688 objects 5531437
Usage: 837388720 delta 496054160 objects 15367983
Usage: 289569928 delta -547818792 objects 4024942
Usage: 308276472 delta 18706544 objects 4569045
Usage: 368364376 delta 60087904 objects 6118254
Usage: 855167840 delta 486803464 objects 15702178
fatal error: runtime: out of memory
goroutine 145 [running]:
runtime.throw(0x593d60)
/home/marco/hg/go/src/pkg/runtime/panic.c:506 +0x69 fp=0x20342a688
runtime.SysMap(0xc2484c0000, 0x100000)
/home/marco/hg/go/src/pkg/runtime/mem_openbsd.c:76 +0x7b fp=0x20342a6b8
runtime.MHeap_SysAlloc(0x5a9580, 0x100000)
/home/marco/hg/go/src/pkg/runtime/malloc.goc:473 +0x103 fp=0x20342a6f8
MHeap_Grow(0x5a9580, 0x10)
/home/marco/hg/go/src/pkg/runtime/mheap.c:243 +0x5c fp=0x20342a738
MHeap_AllocLocked(0x5a9580, 0x1, 0x5)
/home/marco/hg/go/src/pkg/runtime/mheap.c:126 +0x332 fp=0x20342a778
runtime.MHeap_Alloc(0x5a9580, 0x1, 0x5, 0x200000001)
/home/marco/hg/go/src/pkg/runtime/mheap.c:95 +0x7b fp=0x20342a7a0
MCentral_Grow(0x5b0400)
/home/marco/hg/go/src/pkg/runtime/mcentral.c:180 +0x8c fp=0x20342a800
runtime.MCentral_AllocList(0x5b0400, 0x208095060)
/home/marco/hg/go/src/pkg/runtime/mcentral.c:46 +0x4f fp=0x20342a828
runtime.MCache_Refill(0x208095000, 0x5)
/home/marco/hg/go/src/pkg/runtime/mcache.c:22 +0x7c fp=0x20342a848
runtime.mallocgc(0x40, 0x48ec61, 0x1)
/home/marco/hg/go/src/pkg/runtime/malloc.goc:71 +0xff fp=0x20342a8b8
cnew(0x48ec60, 0x40, 0x1)
/home/marco/hg/go/src/pkg/runtime/malloc.goc:713 +0xc1 fp=0x20342a8d8
runtime.cnewarray(0x48ec60, 0x40)
/home/marco/hg/go/src/pkg/runtime/malloc.goc:726 +0x3a fp=0x20342a8f8
makeslice1(0x489f00, 0x20, 0x40, 0x20342a9b0)
/home/marco/hg/go/src/pkg/runtime/slice.c:57 +0x4d fp=0x20342a910
growslice1(0x489f00, 0xc2484b7500, 0x20, 0x20, 0x22, ...)
/home/marco/hg/go/src/pkg/runtime/slice.c:216 +0x58 fp=0x20342a940
runtime.growslice(0x489f00, 0xc2484b7500, 0x20, 0x20, 0x2, ...)
/home/marco/hg/go/src/pkg/runtime/slice.c:183 +0x9d fp=0x20342a988
fmt.(*fmt).fmt_sbx(0xc210024c88, 0x4baea0, 0x0, 0xc2484b74c0, 0x20, ...)
/home/marco/hg/go/src/pkg/fmt/format.go:300 +0x2af fp=0x20342aa00
fmt.(*fmt).fmt_bx(0xc210024c88, 0xc2484b74c0, 0x20, 0x20, 0x4c79d0, ...)
/home/marco/hg/go/src/pkg/fmt/format.go:312 +0x6d fp=0x20342aa48
fmt.(*pp).fmtBytes(0xc210024c30, 0xc2484b74c0, 0x20, 0x20, 0x78, ...)
/home/marco/hg/go/src/pkg/fmt/print.go:588 +0x219 fp=0x20342ab70
fmt.(*pp).printArg(0xc210024c30, 0x489f00, 0xc2484b74e0, 0x78, 0x0, ...)
/home/marco/hg/go/src/pkg/fmt/print.go:806 +0x50f fp=0x20342abd0
fmt.(*pp).doPrintf(0xc210024c30, 0x4baec0, 0x2, 0x20342af30, 0x1, ...)
/home/marco/hg/go/src/pkg/fmt/print.go:1182 +0x14a2 fp=0x20342ae88
fmt.Sprintf(0x4baec0, 0x2, 0x20342af30, 0x1, 0x1, ...)
/home/marco/hg/go/src/pkg/fmt/print.go:235 +0x8e fp=0x20342aed8
main.(*MemTest).start(0xc23366cf20)
/home/marco/git/go/src/ldb/memtest3.go:43 +0x2a6 fp=0x20342af98
runtime.goexit()
/home/marco/hg/go/src/pkg/runtime/proc.c:1386 fp=0x20342afa0
created by main.main
/home/marco/git/go/src/ldb/memtest3.go:78 +0x434
goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc233661f78)
/home/marco/hg/go/src/pkg/runtime/sema.goc:198 +0x30
sync.(*WaitGroup).Wait(0x597f60)
/home/marco/hg/go/src/pkg/sync/waitgroup.go:127 +0x166
main.main()
/home/marco/git/go/src/ldb/memtest3.go:80 +0x205
goroutine 139 [runnable]:
fmt.(*pp).free(0xc2100a8000)
/home/marco/hg/go/src/pkg/fmt/print.go:173
fmt.Sprintf(0x4baec0, 0x2, 0x203420f30, 0x1, 0x1, ...)
/home/marco/hg/go/src/pkg/fmt/print.go:237 +0xd1
main.(*MemTest).start(0xc23366cfe0)
/home/marco/git/go/src/ldb/memtest3.go:43 +0x2a6
created by main.main
/home/marco/git/go/src/ldb/memtest3.go:78 +0x434
goroutine 140 [runnable]:
crypto/sha256.(*digest).Sum(0xc2484ad180, 0x0, 0x0, 0x0, 0x0, ...)
/home/marco/hg/go/src/pkg/crypto/sha256/sha256.go:141 +0x205
main.(*MemTest).start(0xc23366cfc0)
/home/marco/git/go/src/ldb/memtest3.go:42 +0x1db
created by main.main
/home/marco/git/go/src/ldb/memtest3.go:78 +0x434
goroutine 141 [runnable]:
crypto/sha256.(*digest).Write(0x20342ee38, 0x20342ed20, 0x28, 0x40, 0x0, ...)
/home/marco/hg/go/src/pkg/crypto/sha256/sha256.go:105
crypto/sha256.(*digest).checkSum(0x20342ee38, 0x0, 0x0, 0x0, 0x0)
/home/marco/hg/go/src/pkg/crypto/sha256/sha256.go:150 +0x102
crypto/sha256.(*digest).Sum(0xc2484b0780, 0x0, 0x0, 0x0, 0x0, ...)
/home/marco/hg/go/src/pkg/crypto/sha256/sha256.go:137 +0x9d
main.(*MemTest).start(0xc23366cfa0)
/home/marco/git/go/src/ldb/memtest3.go:42 +0x1db
created by main.main
/home/marco/git/go/src/ldb/memtest3.go:78 +0x434
goroutine 142 [runnable]:
unicode/utf8.DecodeRuneInString(0x4baec1, 0x1, 0x4baec0, 0x2)
/home/marco/hg/go/src/pkg/unicode/utf8/utf8.go:229
fmt.(*pp).doPrintf(0xc2100e0000, 0x4baec0, 0x2, 0x20342df30, 0x1, ...)
/home/marco/hg/go/src/pkg/fmt/print.go:1159 +0xfab
fmt.Sprintf(0x4baec0, 0x2, 0x20342df30, 0x1, 0x1, ...)
/home/marco/hg/go/src/pkg/fmt/print.go:235 +0x8e
main.(*MemTest).start(0xc23366cf80)
/home/marco/git/go/src/ldb/memtest3.go:43 +0x2a6
created by main.main
/home/marco/git/go/src/ldb/memtest3.go:78 +0x434
goroutine 143 [runnable]:
crypto/sha256.(*digest).Sum(0xc2484b8280, 0x0, 0x0, 0x0, 0x0, ...)
/home/marco/hg/go/src/pkg/crypto/sha256/sha256.go:141 +0x205
main.(*MemTest).start(0xc23366cf60)
/home/marco/git/go/src/ldb/memtest3.go:42 +0x1db
created by main.main
/home/marco/git/go/src/ldb/memtest3.go:78 +0x434
goroutine 144 [running]:
goroutine running on other thread; stack unavailable
created by main.main
/home/marco/git/go/src/ldb/memtest3.go:78 +0x434
goroutine 146 [runnable]:
crypto/sha256.(*digest).Sum(0xc2484b3380, 0x0, 0x0, 0x0, 0x0, ...)
/home/marco/hg/go/src/pkg/crypto/sha256/sha256.go:141 +0x205
main.(*MemTest).start(0xc23366cf00)
/home/marco/git/go/src/ldb/memtest3.go:42 +0x1db
created by main.main
/home/marco/git/go/src/ldb/memtest3.go:78 +0x434