Скукоживание карт в Go

Не все могут поверить в коварство гошной карты, которая делает кусь не отдает память.

Характерный комментарий:

Если есть сервер на го с сессиями которые реализованы в виде мапы, то даже после отключения клиентов и удаления ключей из нее память не будет освобождаться? 🤌

Штош. Давайте разбираться.

Вот наш клиент с идентификатором и телом в 40 байт:

type Client struct {
    id   uint64
    body [40]byte
}

Создаем карту, добавляем 10К клиентов:

printAlloc("initial")

m := make(map[int]Client)
for i := range 10000 {
    m[i] = Client{id: uint64(i)}
}

runtime.GC()
printAlloc("after create")
initial: heap size = 109 KB
after create: heap size = 1110 KB

Размер кучи вырос до 1100 KB. Удаляем все записи из карты:

for i := range 10000 {
    delete(m, i)
}

runtime.GC()
printAlloc("after delete")
after delete: heap size = 1110 KB

Ни байтика не отдала, зараза!

Попробуем хранить указатели вместо значений:

m := make(map[int]*Client)
for i := range 10000 {
    m[i] = &Client{id: uint64(i)}
}

for i := range 10000 {
    delete(m, i)
}
after create: heap size = 898 KB
after delete: heap size = 429 KB

Почему часть памяти освободилась?

Здесь в памяти хранятся только указатели на клиентов, а сами значения (48B каждое) хранятся вне карты. Поэтому клиентов GC спокойно спокойно освобождает (ссылок-то на них больше нет), а вот внутренние структуры карты по-прежнему занимают память.

Напоследок предположим, что вместо легкого клиента у нас толстенький боди-позитивный с телом на 1024B:

type Client struct {
    id   uint64
    body [1024]byte
}

m := make(map[int]Client)
for i := range 10000 {
    m[i] = Client{id: uint64(i)}
}

for i := range 10000 {
    delete(m, i)
}
after create: heap size = 11683 KB
after delete: heap size = 434 KB

Что за ерунда? Мы же используем значения, а не указатели, почему память освободилась?

Если значения в карте достаточно большие (больше 128B) Go автоматически хранит в карте не сами значения, а указатели на них. Поэтому после GC занятая клиентами память освободилась, и осталась только занятая самой картой память размером 400KB.

Такие дела.

песочница

★ Подписывайтесь на новые заметки.