Як аналізувати пам'ять голангу?


80

Я написав програму golang, яка використовує 1,2 Гб пам'яті під час виконання.

go tool pprof http://10.10.58.118:8601/debug/pprof/heapРезультатом виклику є дамп із використанням лише купи 323,4 МБ.

  • Що з рештою використання пам'яті?
  • Чи є якийсь кращий інструмент для пояснення пам'яті часу виконання golang?

Використовуючи gcvisя отримую це:

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

.. і цей профіль форми купи:

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

Ось мій код: https://github.com/sharewind/push-server/blob/v3/broker


1
Опублікуйте свій код. Розкажіть, чим займається ваша програма.
elithrar

Може, через gc? dave.cheney.net/2014/07/11/visualising-the-go-garbage-collector може допомогти.
VonC

Схоже на те, що пам'ять переробки не збирається і не передається в систему. Це робиться після декількох хвилин бездіяльності. Зачекайте 8 хвилин і перевірте ще раз. Перегляньте це посилання, щоб отримати керівництво щодо налагодження / профілювання програм Go: software.intel.com/en-us/blogs/2014/05/10/…
siritinga

Див. Також runtime.MemStats, пояснені на golang.org/pkg/runtime/#MemStats
Greg Bray,

Відповіді:


66

Профіль купи показує активну пам’ять, пам’ять, яку виконує програма, використовується програмою go (тобто: не зібрана збирачем сміття). Коли GC збирає пам'ять, профіль стискається, але пам'ять в систему не повертається . Ваші майбутні розподіли намагатимуться використовувати пам’ять із пулу раніше зібраних об’єктів, перш ніж запитувати у системи більше.

Зовні це означає, що використання пам'яті вашої програми буде або збільшуватися, або залишатись на рівні. Те, що зовнішня система представляє як "постійний розмір" вашої програми, - це кількість байтів оперативної пам'яті, призначеної вашій програмі, незалежно від того, чи містять вона значення використання go або зібрані.

Причина, чому ці два числа часто досить різні, полягає в тому, що:

  1. Збір пам'яті GC не впливає на зовнішній вигляд програми
  2. Фрагментація пам'яті
  3. GC працює лише тоді, коли використовувана пам'ять подвоює пам'ять, що використовується після попередньої GC (за замовчуванням див .: http://golang.org/pkg/runtime/#pkg-overview )

Якщо ви хочете отримати точну інформацію про те, як Go бачить пам'ять, ви можете використовувати час виконання. ВикликReadMemStats: http://golang.org/pkg/runtime/#ReadMemStats

Крім того, оскільки ви використовуєте веб-профілювання, якщо ви можете отримати доступ до даних профілювання через ваш браузер за адресою:, http://10.10.58.118:8601/debug/pprof/клацнувши посилання купи, ви побачите налагоджувальний вигляд профілю купи, який має роздруковану структуру середовища виконання. знизу.

Документація runtime.MemStats ( http://golang.org/pkg/runtime/#MemStats ) містить пояснення всіх полів, але цікавими для цього обговорення є:

  • HeapAlloc: по суті те, що дає вам профайлер (активна пам'ять купи)
  • Alloc: схожий на HeapAlloc, але для всіх використовується керована пам'ять
  • Sys: загальний обсяг пам’яті (адресного простору), що вимагається від ОС

Все ще існуватимуть розбіжності між системою Sys, і те, про що повідомляє ОС, оскільки те, що Go просить про систему, і те, що ОС дає їй, не завжди однакові. Також пам'ять CGO / syscall (наприклад: malloc / mmap) не відстежується за допомогою go.


Я використовую go 1.3.3 і веб-профілювання, однак /debug/pprof/heapне містить роздруківки середовища виконання. Struktura MemStats
IanB

7
"Жодна пам'ять не повертається в систему" зараз не зовсім точна. Див. Час виконання / налагодження godoc #FreeOSMemory ().
jimx

2
Це могло б бути інакше в минулому, але в відповідності з поточними документами на runtime.MemStats , Allocі HeapAllocмає таке ж значення.
липень

що це означає, коли Sys - це більше, ніж постійна пам’ять? У моєму випадку Alloc становить 778 Мб, Sys - 2326 Мб, а постійна пам’ять - 498 Мб. Я можу зрозуміти, якщо постійна пам’ять перевищує значення Sys, оскільки це означає, що ОС не дала всього, що вимагала програма. Але протилежний сценарій не можна пояснити.
Роганіл

32

Як доповнення до відповіді @Cookie of Nine, коротко: ви можете спробувати цей --alloc_spaceваріант.

go tool pprofвикористовувати --inuse_spaceза замовчуванням. Він вибірково використовує пам’ять, тому результат є підмножиною реального.
За допомогою --alloc_spacepprof повертає всю виділену пам'ять з моменту запуску програми.


1
--alloc_spaceсаме те, що я шукав.
Макс Малиш

20

Мене завжди бентежило зростаюча житлова пам’ять моїх програм 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 на кілька годин).

Аналіз споживання пам'яті

  1. Sys(синонім RSS) кривої досить схожий на HeapSysкриву. Виявляється, динамічне розподіл пам’яті було головним фактором загального зростання пам’яті, тому малий обсяг пам’яті, що споживається змінними стека, здається постійним і його можна ігнорувати;
  2. Постійна кількість програм-програм гарантує відсутність витоків / витоків змінних стека;
  3. Загальна кількість виділених об'єктів залишається незмінною (немає сенсу враховувати коливання) протягом усього процесу.
  4. Найдивовижніший факт: 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.

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

Тим, хто намагається дослідити проблему споживання пам'яті, я рекомендую дотримуватися описаних кроків, щоб виключити деякі тривіальні помилки (наприклад, витік програми).

Явне звільнення пам’яті

Цікаво, що можна значно зменшити споживання пам'яті за допомогою явних викликів debug.FreeOSMemory():

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

порівняння

Насправді такий підхід заощадив близько 35% пам'яті порівняно із умовами за замовчуванням.


7

Ви також можете використовувати StackImpact , який автоматично реєструє та повідомляє на інформаційну панель регулярні та викликані аномалією профілі розподілу пам'яті, які доступні в історичній та порівнянній формі. Докладнішу інформацію про виявлення витоків пам'яті у додатках Production Go див. У цьому щоденнику

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

Застереження: я працюю на StackImpact


Я спробував StackImpact, і витоки пам’яті надзвичайно зросли. Один із пунктів витоку пам'яті pastebin.com/ZAPCeGmp
Влад

Схоже, ви використовуєте --alloc_space, що не підходить для виявлення витоків пам'яті. Він просто покаже вам, скільки пам'яті було виділено з моменту запуску програми. Для довготривалої програми цифри можуть стати досить високими. На сьогодні нам не відомо про витоки пам'яті в агенті StackImpact.
logix
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.