LINUX.ORG.RU

Linux C++ free memory after dealocation

 , , , ,


1

5

Привет форумчане. Странные вещи творятся в с++ и системе.

Если контейнер(такой чтоб лежал последовательно в памяти) наполнить то память естественно вырастет. Но если потом даже удалить из контейнера все элементы, то потребление памяти не снижается. Если проверить память дважды, то на второй раз потребление памяти незначительно снижается на некоторых контейнерах. Оно понятно, что plain контейнеры резервирую память на будующее для уменьшения аллокаций. Но даже если контейнеру сделать swap с пустым, то потребление памяти всё равно не уменьшается. Смею предположить, что это ещё какая-то системная фича, мол если процесс затребовал себе гектар, потом освободил его, то система не забирает его сразу обратно, мало-ли ещё понадобится. Я сделал такой вывод ещё потому, что даже если выделаю через new много памяти, то после delete освобождается только половина.

Мои потуги можно увидеть тут https://github.com/LuxoftAKutsan/TestsAndResearch/tree/master/free_memory_smo...

Вопросы к форумчанам :

1) Где почитать матчасть по этой теме.

2) Может я лажаю в коде и у меня реальные утечки?

3) Как бы заставить систему вернуть себе более не используемую мной память.

Почитать за реализацию контейнеров.

anonymous ()

Утечки нужно через valgrind смотреть. Всё остальное от лукавого.

Ну и нужно использовать shrink (или как там у вашего контейнера), если хотите уменьшить реальное потребление.

RazrFalcon ★★★★★ ()

Luxoft

А, ну понятно.

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

Valgrind не показывает утечек. Shrink работает для вектора ( как и swap с пустым)

Меня больше всего волнует queue но вот именно она не хочет очищаться. shrinka у неё нет, но для меня остаётся заадкой почему не помогает swap с пустой очередью

Queue Test
Memory usage : 1.78 Mb.
Filled. size = 10000000
Memory usage : 802.388 Mb.
Freed. size = 0
Memory usage : 802.388 Mb.
Memory usage : 802.388 Mb.
Queue freed with swap
Memory usage : 802.388 Mb.
Fill secondary
Filled. size = 10000000
Memory usage : 800.44 Mb.
Freed. size = 0
Memory usage : 800.44 Mb.
Memory usage : 800.44 Mb.

dokihot ()
Ответ на: комментарий от RazrFalcon

С deque тоже какая-то магия. swap освобождает какие-то панты памяти


Deque Test
Memory usage : 1.748 Mb.
Filled. size = 10000000
Memory usage : 800.296 Mb.
Freed. size = 0
Memory usage : 800.296 Mb.
Fill secondary
Filled. size = 10000000
Memory usage : 800.284 Mb.
Queue freed with swap
Memory usage : 794.356 Mb. // тут по идее уже должно быть всё свободно

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

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

⤇⥰⥰⥰⥰⥰⥰⥰free_container(q);¬                                  
⤇⥰⥰⥰⥰⥰⥰⥰usleep(time_to_sleep);¬                              
⤇⥰⥰⥰⥰⥰⥰⥰print_memory_usage();¬                               
⤇⥰⥰⥰⥰⥰⥰⥰print_memory_usage();¬ 

Вывод:

Freed. size = 0
Memory usage : 800.3 Mb.
Memory usage : 9.608 Mb.


Что происходит вообще тут? 
dokihot ()
Ответ на: комментарий от dokihot

потому что std::queue чаще всего реализован на std::deque

Шрынка у него нет в силу его реализации. Если очень чешется, то после удаления всех элементов, делай swap с пустым контейнером.

anonymous ()

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

Это аллокатор в libc так работает. В случае фрагментации кучи огромные пустые куски могут оставаться заюзанными процессом, хотя в программе они были явно удалены

Но аллокатор может пометить любую страницу (обычно 4К) посреди кучи как свободную, тогда ядро сможет ее использовать для других нужд, до тех пор пока в нее не будет произведена запись

annulen ★★★★★ ()

free (и работающий поверх него плюсовый delete) не обязан сразу возвращать память в систему. Смотри исходники glibc и можешь поиграться с mallopt

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

Исходники это прекрастно. Но это где-то документировано? И можно ли как-то попросить его его вернуть память обратно системе?

dokihot ()
Ответ на: комментарий от tailgunner

Современные аллокаторы обычно используют mmap.

malloc из glibc использует и sbrk, и mmap.

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

Хз. Я вообще стараюсь избегать std, ибо он весь на честном слове работает.

QVector::squeeze() гарантированно и одинаково работает на всех ОС.

RazrFalcon ★★★★★ ()

Luxoft

ля... шаражкина контора

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

Аллокатор из glibc к современным относится?

Скорее да, чем нет, и он может использовать mmap (правда с настройками по-умолчанию делает это неохотно)

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

кто тебе сказал такую глупость, что аллокатор не может выделять память через mmap?

Может. А вот освобождать через munmap не всегда возможно.

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

И можно ли как-то попросить его его вернуть память обратно системе?

Аллокатор можно настраивать, можно заменить на сторонний (и настраивать уже его:), можно использовать memory pool'ы в своем приложении для определенных типов данных

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

Программисты на Qt уже написали и отдыхают, а хардкорные посоны на чиста конкретных плюсах всё ещё подкидывают монетку чтобы определить поведение.

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

Программисты на Qt уже написали и отдыхают, а хардкорные посоны на чиста конкретных плюсах всё ещё подкидывают монетку чтобы определить поведение.

Программисты на Qt уже написали и со своим Q-говном по производительности отдыхают, а хардкорные посоны на чиста конкретных плюсах всё ещё могут быть быстрее джавы.

anonymous ()

Стандартный аллокатор использует sbrk и mmap по своему усмотрению, чтобы сообщить ядру, какая память используется данным процессом. Вызовы malloc, free, new, delete - внутреннее дело процесса, ядру неведомое.

Если хотите более детерминированное выделение и освобождение памяти, то вам нужен свой собственный аллокатор. Например, он мог бы делать ядру mmap на каждый ваш new. Будет не очень эффективно, но зато предсказуемо.

Deleted ()
Ответ на: комментарий от RazrFalcon

Вы же сами выше привели пример с shrink_to_fit.

Это, конечно, неприятно, но вполне логично. Мало ли какой там системный аллокатор окажется - если он не захочет память отдавать, то что shrink_to_fit сделать может?

Да и в пределах Qt тоже не всегда получается оставаться - не даром же у них имеется возможность добраться до нижележащей реализации.

Ну и если есть ещё примеры, то было бы интересно их услышать.

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

не даром же у них имеется возможность добраться до нижележащей реализации

Это у каких классов/контейнеров? В большинстве случаев вся реализация приколочена намертво, что иногда тоже не очень удобно.

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

Это у каких классов/контейнеров?

Для контейнеров (наверное) нет. Зато есть всякие nativeEvent, winId и т.д. Ну и плюс разные дефайны для определения платформы.

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

Ну это не контейнеры. И нужно оно только для доступа к ОС-специфичным вещам.

Если брать классы, которые пересекаются с std - то тут всё залоченно полностью. В угоду удобства использования. Ну и надёжности.

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

Ну и каждого компилятора/ОС свои реализации

...зачастую заточенные под особенности оптимизатора и интринсики конкретного компилятора

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

Но аллокатор может пометить любую страницу (обычно 4К) посреди кучи как свободную,

В теории вообще не имеет значения. Потому что неиспользуемая страница, когда системе понадобиться память, уйдет в swap, а виртуальной памяти на 64-х битных системах предостаточно. Это я к тому, что плевать, отдает аллокатор память Операционной Системе или нет, если не отдает, то разница лишь в наличии дескриптора страницы памяти (40) байт в ядре и занятом месте на диске + занятый участок виртуального адресного пространства (2^48). Хотя, лучше, конечно, отдавать память, что и сделает любой из современных аллокаторов памяти при наличии возможности и превышении размера непрерывного куска свободной памяти определенного граничного размера (около 1М по умолчанию).

В общем задача задротско-теоретическая, не ходи туда работать. Или, по-крайней мере, покажи им, что ты не задрот, иначе у них сложится впечатление, что лучший способ мотивировать тебя - это начать чморить (типа ты нечего не умеешь - докажи, что ты не лох).

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

Потому что неиспользуемая страница, когда системе понадобиться память, уйдет в swap

Во встроенной системе запросто моджет не быть свопа, да и в любом случае его использование надо минимизировать

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

В теории вообще не имеет значения. Потому что неиспользуемая страница, когда системе понадобиться память, уйдет в swap

Это какая-то уж слишком абстрактная теория, когда уход страницы в своп не имеет значения. :)

Хотя, лучше, конечно, отдавать память, что и сделает любой из современных аллокаторов памяти при наличии возможности и превышении размера непрерывного куска свободной памяти определенного граничного размера (около 1М по умолчанию).

Стандартный линуксовый glibc'шный аллокатор так не делает. Как уже сказали выше, для небольших кусочков памяти он использует brk(), и не торопится отдавать память системе. Вот jemalloc - делает, - он использует mmap() и своевременно возвращает память системе посредством madvise(DONTNEED).

Sorcerer ★★★★★ ()

ОС не умеет забирать обратно меньше чем полную страницу. Ещё возможно взаимодействие через просто диапазон адресов без учёта освобождения в середине.

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

неиспользуемая страница, когда системе понадобиться память, уйдет в swap

И как система об этом узнает, если страница использовалась?

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