Подтвердить что ты не робот

Как анализировать голанг-память

Я написал программу golang, она стоила 1,2 ГБ во время выполнения. когда я использую

go tool pprof http://10.10.58.118:8601/debug/pprof/heap

чтобы получить дамп.

Он показывает только 323.4MB в куче. ушла ли другая память?

Есть ли какой-нибудь лучший инструмент для объяснения памяти во время выполнения golang?


когда я использую gcvis, я получил это

enter image description here

и профиль формы кучи enter image description here

мой код на

https://github.com/sharewind/push-server/blob/v3/broker
4b9b3361

Ответ 1

В профиле кучи отображается активная память, память, которую, как считается, использует программа go (т.е. не была собрана сборщиком мусора). Когда GC собирает память, профиль сжимается, но память не возвращается в систему. Ваши будущие ассигнования будут пытаться использовать память из пула ранее собранных объектов, прежде чем запрашивать у системы больше.

Извне это означает, что использование вашей программной памяти будет либо увеличиваться, либо оставаться на уровне. То, что внешняя система представляет в качестве "Резидентного размера" вашей программы, - это количество байтов ОЗУ, назначенное вашей программе, независимо от того, удерживает ли она текущие значения или собираемые значения.

Причина, по которой эти два числа часто сильно различаются, объясняется тем, что:

  • Накопительная память GC не влияет на внешний вид программы
  • Фрагментация памяти
  • GC работает только тогда, когда используемая память удваивает память, используемую после предыдущего GC (по умолчанию см. http://golang.org/pkg/runtime/#pkg-overview)

Если вам нужна точная разбивка того, как Go видит память, вы можете использовать runtime.ReadMemStats: http://golang.org/pkg/runtime/#ReadMemStats

В качестве альтернативы, поскольку вы используете профилирование через Интернет, если вы можете получить доступ к профилирующим данным через ваш браузер по адресу: http://10.10.58.118:8601/debug/pprof/, щелчок по ссылке "куча" покажет вам отладочный вид профиля кучи, который имеет распечатку runtime.MemStats внизу.

Документация runtime.MemStats(http://golang.org/pkg/runtime/#MemStats) содержит объяснение всех полей, но интересными для этого обсуждения являются:

  • HeapAlloc: по существу то, что дает вам профилировщик (активная память кучи)
  • Alloc: похоже на HeapAlloc, но для всех управляемых управляемых файлов
  • Sys: общий объем памяти (адресного пространства), запрашиваемый у операционной системы

По-прежнему будут возникать расхождения между Sys и то, что сообщает ОС, потому что то, что Go спрашивает о системе, и то, что дает ОС, не всегда одинаковы. Также память CGO/syscall (например: malloc/mmap) не отслеживается go.

Ответ 2

Как дополнение к ответу @Cookie of Nine, одним словом: вы можете попробовать вариант --alloc_space.

go tool pprof используйте --inuse_space по умолчанию. Он отображает использование памяти, поэтому результатом является подмножество реального.
В --alloc_space pprof возвращает всю выделенную память с момента запуска программы.

Ответ 3

Меня всегда путали в растущей памяти о моих приложениях Go, и, наконец, мне пришлось изучить инструменты профилирования, которые присутствуют в экосистеме Go. Runtime предоставляет много показателей в структуре runtime.Memstats, но может быть трудно понять, кто из них может помочь выяснить причины памяти роста, поэтому необходимы некоторые дополнительные инструменты.

Профилирование

Используйте https://github.com/tevjef/go-runtime-metrics в своем приложении. Например, вы можете поместить это в свой main:

import(
    metrics "github.com/tevjef/go-runtime-metrics"
)
func main() {
    //...
    metrics.DefaultConfig.CollectionInterval = time.Second
    if err := metrics.RunCollector(metrics.DefaultConfig); err != nil {
        // handle error
    }
}

Запустите InfluxDB и Grafana в контейнерах Docker:

docker run --name influxdb -d -p 8086:8086 influxdb
docker run -d -p 9090:3000/tcp --link influxdb --name=grafana grafana/grafana:4.1.0

Настроить взаимодействие между Grafana и InfluxDB Grafana (главная страница Grafana → верхний левый угол → Источники данных → Добавить новый источник данных):

введите описание изображения здесь

Импортировать панель # 3242 из https://grafana.com (Главная страница Grafana → верхний левый угол → панель инструментов → импорт):

введите описание изображения здесь

Наконец, запустите приложение: оно передаст показатели времени выполнения в созданный InfluxDB. Положите ваше приложение под разумную нагрузку (в моем случае это было довольно мало - 5 RPS в течение нескольких часов).

Анализ потребления памяти

  • Sys (синоним RSS) кривая очень похожа на кривую HeapSys. Оказывается, динамическое распределение памяти было основным фактором роста общей памяти, поэтому небольшой объем памяти, потребляемый переменными стека, кажется постоянным и может быть проигнорирован;
  • Постоянное количество goroutines гарантирует отсутствие утечек goroutine/утечек стека,
  • Общее количество выделенных объектов остается неизменным (нет смысла учитывать флуктуации) в течение всего жизненного цикла процесса.
  • Самый удивительный факт: HeapIdle растет с той же скоростью, что и Sys, а HeapReleased всегда равно нулю. Очевидно, что среда выполнения не возвращает память в ОС вообще, по крайней мере, в условиях этого теста:
HeapIdle minus HeapReleased estimates the amount of memory    
that could be returned to the OS, but is being retained by
the runtime so it can grow the heap without requesting more
memory from the OS.

введите описание изображения здесь введите описание изображения здесь

Для тех, кто пытается исследовать проблему потребления памяти, я бы рекомендовал следовать описанным шагам, чтобы исключить некоторые тривиальные ошибки (например, утечку goroutine).

Свободно освобождать память

Интересно, что можно значительно уменьшить потребление памяти с явным вызовом debug.FreeOSMemory():

// in the top-level package
func init() {
   go func() {
       t := time.Tick(time.Second)
       for {
           <-t
           debug.FreeOSMemory()
       }
   }()
}

сравнение

Фактически, этот подход сэкономил около 35% памяти по сравнению с условиями по умолчанию.

См. также

https://github.com/golang/go/issues/14521

Ответ 4

Вы также можете использовать StackImpact, который автоматически записывает и отправляет регулярные и аномальные профили распределения памяти на панель инструментов, которые доступны в исторической и сопоставимой форме. См. Это сообщение в блоге для более подробной информации Обнаружение утечки памяти в приложениях для запуска производства

введите описание изображения здесь

Отказ от ответственности: я работаю для StackImpact