Освобождение памяти в go

Два вопроса за день - это конечно... В общем всё ещё есть прога из нескольких сайтов и чата. Для теста запустили, накинули стресс-тест. Живёт хорошо, конечно, но возникла проблема: вот произошла нагрузка, прога занимает память (для чтения шаблонов и конфиг-json'ов), нагрузка прошла, а память не освобождается. Сначала думал где-то не закрыл файл или http.Request, но перепроверив несколько раз убедился, что всё закрывается. Полез гуглить, проср**сь много времени, в итоге в какой-то из статей вижу, что GC освобождает память внутри программы, но отнимать её у системы не перестаёт. Опять же уже традиционный вопрос: С этим что-то можно сделать? (Чтобы память чистилась по-человечески) Если нет - что будет после того, как программа займёт всю доступную память?


Ответы (1 шт):

Автор решения: Pak Uula

Для того, чтобы принудительно вернуть свободную память обратно операционной системе, есть функция FreeOSMemory из пакета runtime/debug

GC освобождает память внутри программы, но отнимать её у системы не перестаёт

Документация на FreeOSMemory с вами не согласна:

the runtime gradually returns memory to the operating system in a background task

Количество свободной памяти можно узнать, вызвав функцию ReadMemStats. В выходном параметре типа MemStats поле HeapIdle содержит число байтов в чанках памяти, в которых нет объектов Go. То есть это та память, которую, теоретически, можно отдать обратно операционной системе.

Вот маленький пример использования. Программа создает буфер размером 1 МБ, а затем забывает его и подчищает память.

package main

import (
        "fmt"
        "runtime"
        "runtime/debug"
)

// Allocate 1 MB
var big = make([]byte, 1<<20)

func main() {
        var ms1, ms2 runtime.MemStats

        // Drop the link to 1 MB
        big = nil
        // Force GC
        runtime.GC()
        runtime.ReadMemStats(&ms1)
        // Force memory release
        debug.FreeOSMemory()
        runtime.ReadMemStats(&ms2)
        fmt.Println("1MB in bytes: ", 1<<20)
        fmt.Println("Idle memory before: ", ms1.HeapIdle)
        fmt.Println("Idle memory after: ", ms2.HeapIdle)
        fmt.Println("Idle memory delta: ", int64(ms2.HeapIdle)-int64(ms1.HeapIdle))
        fmt.Println("Released memory before: ", ms1.HeapReleased)
        fmt.Println("Released memory after: ", ms2.HeapReleased)
        fmt.Println("Released memory delta: ", ms2.HeapReleased - ms1.HeapReleased)
}

У меня go 1.15 вот какие результаты:

1MB in bytes:  1048576
Idle memory before:  66232320
Idle memory after:  66265088
Idle memory delta:  32768
Released memory before:  65110016
Released memory after:  66265088
Released memory delta:  1155072

В результате вызова FreeOSMemory операционной системе были возвращены 1МБ с хвостиком.

→ Ссылка