The following program has live set of at most 100MB . With GODEBUG=gcstoptheworld=1 RSS stabilizes at ~204MB according to top, which makes sense. However with GODEBUG=gcstoptheworld=0 RSS steadily chaotically grows over time, I waited only for few minutes and RSS grew to 1106MB, one of the jumps was from 212MB to 678MB in one second. GOMAXPROCS value may have effect on this, I've get the worst numbers with GOMAXPROCS=2/4, however I did not make an exhaustive investigation, maybe GOMAXPROCS is red herring.
package main
import (
"math/rand"
"sync"
"sync/atomic"
"unsafe"
)
var (
gs, retained, seed int32
mu sync.Mutex
blocks [][]*byte
sink = make([]byte, 4096)
)
const (
maxGs = 100
maxRetained = 100 << 20
maxBlock = 64 << 10
)
func main() {
go worker()
select {}
}
func worker() {
rnd := rand.New(rand.NewSource(int64(atomic.AddInt32(&seed, 1))))
ss := unsafe.Sizeof(&gs)
var local [][]*byte
loop:
for {
switch r := rnd.Intn(1000); {
case r == 0 || r == 1:
if !tryAdd(&gs, 1, maxGs, 1) {
continue loop
}
go worker()
case r == 2:
if !tryAdd(&gs, 1, maxGs, -1) {
continue loop
}
s := 0
for _, b := range local {
s += len(b)
}
local = nil
if !tryAdd(&retained, 0, maxRetained, -int32(s)*int32(ss)) {
panic("bad")
}
return
case r < 300:
s := maxBlock / int(ss)
if !tryAdd(&retained, 0, maxRetained, int32(s)*int32(ss)) {
continue loop
}
b := make([]*byte, s)
p := &sink[len(sink)-1]
for i := range b {
b[i] = p
}
mu.Lock()
blocks = append(blocks, b)
mu.Unlock()
case r < 500:
s := 0
mu.Lock()
if len(blocks) > 0 {
idx := rnd.Intn(len(blocks))
s = len(blocks[idx])
blocks[idx] = blocks[len(blocks)-1]
blocks[len(blocks)-1] = nil
blocks = blocks[:len(blocks)-1]
}
mu.Unlock()
if !tryAdd(&retained, 0, maxRetained, -int32(s)*int32(ss)) {
panic("bad")
}
case r < 800:
s := maxBlock / int(ss)
if !tryAdd(&retained, 0, maxRetained, int32(s)*int32(ss)) {
continue loop
}
b := make([]*byte, s)
p := &sink[len(sink)-1]
for i := range b {
b[i] = p
}
local = append(local, b)
default:
s := 0
if len(local) > 0 {
idx := rnd.Intn(len(local))
s = len(local[idx])
local[idx] = local[len(local)-1]
local[len(local)-1] = nil
local = local[:len(local)-1]
}
if !tryAdd(&retained, 0, maxRetained, -int32(s)*int32(ss)) {
panic("bad")
}
}
}
}
func tryAdd(p *int32, min, max, v int32) bool {
for {
old := atomic.LoadInt32(p)
xchg := old + v
if xchg < min || xchg > max {
return false
}
if atomic.CompareAndSwapInt32(p, old, xchg) {
return true
}
}
}
go version devel +d0729a6 Tue Jul 28 06:23:38 2015 +0000 linux/amd64
The following program has live set of at most 100MB . With GODEBUG=gcstoptheworld=1 RSS stabilizes at ~204MB according to top, which makes sense. However with GODEBUG=gcstoptheworld=0 RSS steadily chaotically grows over time, I waited only for few minutes and RSS grew to 1106MB, one of the jumps was from 212MB to 678MB in one second. GOMAXPROCS value may have effect on this, I've get the worst numbers with GOMAXPROCS=2/4, however I did not make an exhaustive investigation, maybe GOMAXPROCS is red herring.
go version devel +d0729a6 Tue Jul 28 06:23:38 2015 +0000 linux/amd64