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