LINUX.ORG.RU

Работа с памятью в go

 ,


1

2

premetheus по процессу отдаёт метрики:

go_memstats_heap_idle_bytes

go_memstats_heap_released_bytes

при этом эти значения около 13GB для сервера с 16GB RAM. $ps эту память включает в RSS процесса, так же как $free этих свободных 13GB не видит, нет её ни в кэше, ни в буферах. В meminfo эта память числится в Inactive(anon). Собственно вопрос, когда эта память станет реально доступной для ОС?



Последнее исправление: Viper (всего исправлений: 1)

Когда ОС её затребует. Го держит освободившуюся память за собой, пока её никто спрашивает.

beastie ★★★★★
()
Последнее исправление: beastie (всего исправлений: 1)
Ответ на: комментарий от anonymous

По терминалогии сейчас ничего не сказу, забыл уже всё, какие там флаги на станицах стоят. Но в общем это, это как кеш. ОС может забрать этот пустой кеш себе обратно в любой момент при необходимости. Но в общем эта пустая память остаётся за Го процессом для дальнейшего использования. Го постепенно возвращает пустую память обратно ОС, если ему так много больше не надо.

См. например debug.FreeOSMemory

И тут больше https://blog.golang.org/ismmkeynote

beastie ★★★★★
()
Последнее исправление: beastie (всего исправлений: 1)
Ответ на: комментарий от anonymous

Поддержу вопрос. Как процесс узнаёт что память нужна, что бы её окончательно отдать и не держать за собой? А учитывая, что этой памяти нет среди available для ОС, без согласия процесса, который за собой держит память, не может ОС просто её взять и использовать. По-моему что-то подобное работает и в ZFS для ARC. То же говорят, свободную память держит за собой, только вот не всегда охотно её отдаёт ОС, по этому приходится ограничивать размер ARC в ZFS.

P.S. Процесс go уже более суток не отдаёт эту память, хотя судя по метрикам, она ему больше не нужна, так как вся она в go_memstats_heap_released_bytes без значительных изменений этой метрики.

Viper
() автор топика
Последнее исправление: Viper (всего исправлений: 2)
Ответ на: комментарий от Viper

Исходя из ответа beastie, предположу, что можно запустить какой-то процесс, который начнет жрать память, и ОС потихоньку начнет забирать ее у go, и передавать этому процессу.

anonymous
()
Ответ на: комментарий от Viper

В ТС попике есть хинт: «В meminfo эта память числится в Inactive(anon).»

Inactive(anon) — The amount of anonymous and tmpfs/shmem memory, in kibibytes, that is a candidate for eviction.

Т.е. страницы помечены как неактивные. Дальше уже надо вспоминать, как вся эта лабуда в ядре работает. Я тут немного заржавел. Могу соврать. Но в общем это алоциированные страницы (посчитанные за процессом, виртуальная память), которые никому больше не нужны (свободны для дальнейшего использования, реальная память).

Хотя eviction – было вроде про swap, но может я что-то путаю.

В любом случае, высокие значения go_memstats_heap_idle_bytes / go_memstats_heap_released_bytes – это «нормально» и не повод парится.

Не забываем, что и на 8MB машине можно аллоциировать 16GB (виртуальной) памяти. Виртуальная память она такая. Пока в неё не пишешь, аллоциируй что хочешь.

beastie ★★★★★
()
Последнее исправление: beastie (всего исправлений: 3)
Ответ на: комментарий от yyk

Конечно не всё равно. Хотелось что-бы эта свободная память использовалась ОС для файлового кэша, а она для него не используется, так как закреплена за процессом go, но go её в свою очередь то же не использует, а бережёт её для будущего использования, если понадобится, но вот «если понадобится» может и не наступить, а использование для файлового кэша всегда полезно.

Viper
() автор топика
Ответ на: комментарий от beastie

Я парюсь из-за того, что она не используется файловым кэшем. А go, возможно, она вообще никогда больше не понадобится. Inactive(anon) я всегда считал, что эта та память, которая кандидат в первую очередь на выгрузку в swap, а не просто не используемая. То есть в ней есть и те данные, которые реально могут понадобиться и нельзя её просто взять и перетереть. Возможно, когда ОС захочет эту память отправить в swap, процесс go получит какое-то событие (но я о таком не знаю, знаю только обратное действие pegefault), и отреагирует её полным освобождением и ОС не нужно будет её в swap переносить.

Viper
() автор топика
Ответ на: комментарий от Viper

Когда я в первый раз на это наткнулся, пару лет назад, то тоже был немного в замешательстве. Но в общем, это не повод к беспокойству.

beastie ★★★★★
()
Ответ на: комментарий от beastie

В общем, то что go явно не отдаёт память это плохо для моей ситуации, так как файловый кэш эту свободную память реально не может использовать, падает производительность ввода/вывода. Так же есть странности и с отдачей памяти операционке. Написал небольшой скрипт на Python, который заполняет память своими данными. В результате память процессом go была отдана, но это я вижу по значению RSS в команде ps, при этом счётчики:
go_memstats_heap_idle_bytes
go_memstats_heap_released_bytes
остались без изменений. Может у процесса go по умолчанию есть обработка какого-то сигнала, который бы инициировал отдачу памяти ОС?

Viper
() автор топика
Ответ на: комментарий от Viper

Это скорее от общего недопонимания, как работает память. (У меня этого полного понимания тоже нет.)

It turns out that there was a change in Go 1.12 regarding how the runtime signals the operating system that it can take unused memory. Before Go 1.12, the runtime sends a MADV_DONTNEED signal on unused memory and the operating system immediately reclaims the unused memory pages. Starting with Go 1.12, the signal was changed to MADV_FREE, which tells the operating system that it can reclaim some unused memory pages if it needs to, meaning it doesn’t always do that unless the system is under memory pressure from different processes.

Unless you have other services that are running and are also memory-hungry, the RSS (Resident Set Size), which is basically the apparent amount of memory that the service is consuming, will not drop.

According to the issues on the Go repository, this is apparently a problem only on iOS, not on Linux, and yet we were experiencing the same problem. We then found out that we could run our Go service with this flag GODEBUG=madvdontneed=1 to force the runtime to use the MADV_DONTNEED signal instead of MADV_FREE. We decided to give this a try!

https://blog.detectify.com/2019/09/05/how-we-tracked-down-a-memory-leak-in-one-of-our-go-microservices/

Вот хорошее объяснение, как go сигнализирует os, что память больше не нужна. И почему os забирает эту память себе обратно, как левая пятка пожелает.

beastie ★★★★★
()
Ответ на: комментарий от beastie

Спасибо, статья расставила всё на свои места. Только для того, что бы понять что это не утечка, у них много усилий ушло. :-)
В общем с версии 1.16 вернули всё взад, «проблема» была начиная с версии 1.12. Вот тут причина, по которой вернулись к тому, что было:
https://github.com/golang/go/issues/42330
Только почему-то не учли ещё один минус такого поведения, это то, что как-бы «освобождённая память» не используется файловым кэшем, или я не правильно экспериментировал.

Viper
() автор топика
Ответ на: комментарий от Viper

Когда я в первый раз на это наткнулся (примерно в тоже время, что и статья), я тоже изначально думал, что словил memory leak. ;)

beastie ★★★★★
()
Ответ на: комментарий от Viper

возможно, она вообще никогда больше не понадобится. Inactive(anon) я всегда считал, что эта та память, которая кандидат в первую очередь на выгрузку в swap, а не просто не используемая. То есть в ней есть и те данные, которые реально могут понадобиться и нельзя её просто взять и перетереть.

и тут в тред должны были ворваться свидетели жизни без свапа, но они куда-то подевались, ведь это именно то, что делает вменяемый менеджер VFS, эти страницы могут и не понадобиться никогда после загрузки системы или приложения, но, так же, эти страницы могут быть и разделяемой памятью или понадобиться позже.

просто мимокрокодил про swap и анонимную память

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.