Skip to content

Commit 9980b70

Browse files
committed
runtime: limit the number of map overflow buckets
Consider repeatedly adding many items to a map and then deleting them all, as in #16070. The map itself doesn't need to grow above the high water mark of number of items. However, due to random collisions, the map can accumulate overflow buckets. Prior to this CL, those overflow buckets were never removed, which led to a slow memory leak. The problem with removing overflow buckets is iterators. The obvious approach is to repack keys and values and eliminate unused overflow buckets. However, keys, values, and overflow buckets cannot be manipulated without disrupting iterators. This CL takes a different approach, which is to reuse the existing map growth mechanism, which is well established, well tested, and safe in the presence of iterators. When a map has accumulated enough overflow buckets we trigger map growth, but grow into a map of the same size as before. The old overflow buckets will be left behind for garbage collection. For the code in #16070, instead of climbing (very slowly) forever, memory usage now cycles between 264mb and 483mb every 15 minutes or so. To avoid increasing the size of maps, the overflow bucket counter is only 16 bits. For large maps, the counter is incremented stochastically. Fixes #16070 Change-Id: If551d77613ec6836907efca58bda3deee304297e Reviewed-on: https://go-review.googlesource.com/25049 Run-TryBot: Josh Bleecher Snyder <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Keith Randall <[email protected]>
1 parent 0cd3ecb commit 9980b70

File tree

3 files changed

+212
-78
lines changed

3 files changed

+212
-78
lines changed

src/cmd/compile/internal/gc/reflect.go

+12-10
Original file line numberDiff line numberDiff line change
@@ -182,20 +182,22 @@ func hmap(t *Type) *Type {
182182
}
183183

184184
bucket := mapbucket(t)
185-
var field [8]*Field
186-
field[0] = makefield("count", Types[TINT])
187-
field[1] = makefield("flags", Types[TUINT8])
188-
field[2] = makefield("B", Types[TUINT8])
189-
field[3] = makefield("hash0", Types[TUINT32])
190-
field[4] = makefield("buckets", Ptrto(bucket))
191-
field[5] = makefield("oldbuckets", Ptrto(bucket))
192-
field[6] = makefield("nevacuate", Types[TUINTPTR])
193-
field[7] = makefield("overflow", Types[TUNSAFEPTR])
185+
fields := []*Field{
186+
makefield("count", Types[TINT]),
187+
makefield("flags", Types[TUINT8]),
188+
makefield("B", Types[TUINT8]),
189+
makefield("noverflow", Types[TUINT16]),
190+
makefield("hash0", Types[TUINT32]),
191+
makefield("buckets", Ptrto(bucket)),
192+
makefield("oldbuckets", Ptrto(bucket)),
193+
makefield("nevacuate", Types[TUINTPTR]),
194+
makefield("overflow", Types[TUNSAFEPTR]),
195+
}
194196

195197
h := typ(TSTRUCT)
196198
h.Noalg = true
197199
h.Local = t.Local
198-
h.SetFields(field[:])
200+
h.SetFields(fields)
199201
dowidth(h)
200202
t.MapType().Hmap = h
201203
h.StructType().Map = t

0 commit comments

Comments
 (0)