LINUX.ORG.RU

Разбухание памяти в программах на C/C++ в Linux


0

0

Я на днях прочитал, что реализация функций malloc и free в glibc не выполняет никакой работы по сокращению объёма используемой памяти, не считая системного вызова brk() или как там его, в результате чего получается фрагментация используемой программой оперативной памяти.

Интересно стало, как с этим можно бороться и как вообще люди борются?

Кстати, прошёл слушок, что возможно заюзать реализацию malloc из openbsd, неужели правда?

P.S. Вопрос навеян тенденцией всё большего и большего пожирания памяти линуксовыми програмами, например, firefox, потребление памяти которым просто легендой стало.

★★

*alloc/free используют mmap/munmap для выделения/освобождения кусков > M_MMAP_THRESHOLD (см. malloc.h и http://www.delorie.com/gnu/docs/glibc/libc_32.html), mmapнутая память возвращается ядру при free(). При sbrk(-...) тоже возврашается.

> Кстати, прошёл слушок, что возможно заюзать реализацию malloc из openbsd, неужели правда?

http://kerneltrap.org/node/5584 , не знаю, поможет ли это firefox, если прикрутить это к нему вместо *alloc/free из glibc.

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

Вот только с этими константками непонятка одна есть: по инфе, разбросанной в интернете и по файлу arena.c из сырцов glibc можно сделать вывод, что это же можно сделать установкой соответствующих переменных окружения, типа export MALLOC_MMAP_THRESHOLD_=1024 -- но установка этой и других переменных абсолютно ничего не меняет! Правда скорее всего, просто моя glibc без этой фичи скомпилена (grep MALLOC_MMAP_THRESHOLD_ -r `rpm -ql glibc` ничего не выдаёт).

В общем да, мне надо не тормозить и попробовать вбить mallopt(M_MMAP_THRESHOLD, xxxx) в конкретную программку...

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

Написал простейшую тестовую программку, убедился, что когда её запускаешь командой MALLOC_MMAP_THRESHOLD_=1000 ./a.out или вставляешь mallopt(M_MMAP_THRESHOLD, 1000) -- память ведёт себя совсем по другому по сравнению с просто ./a.out. Значит, всё-таки меняет.

Это firefox меня в заблуждение ввёл, там мало что меняется с уменьшением этого параметра, по крайней мере на первый взгляд, только наоборот стартовая память увеличивается. Вообще не понятно, они там в firefox что, все новые объекты объёмом по несколько байтов создают, что ли? или они какой-то свой malloc используют? Вполне возможно, у них там в каталоге какие-то подозрительные файлы есть:

% find -iname "*malloc*" ./nsprpub/pr/src/malloc ./nsprpub/pr/src/malloc/prmalloc.c ./nsprpub/pr/tests/dbmalloc.c ./nsprpub/pr/tests/dbmalloc1.c ./gc/boehm/malloc.c ./gc/boehm/mallocx.c ./gc/boehm/real_malloc.c ./tools/leaky/libmalloc.cpp ./tools/leaky/libmalloc.h ./xpcom/build/malloc.c ./gfx/cairo/cairo/test/xmalloc.c ./gfx/cairo/cairo/test/xmalloc.h

Надо с этим разобраться.

mr ★★
() автор топика

syscall brk() таки как раз устанавливает нижнюю границу адресного пространства процесса... и если в free вставить код реализующий "хвостовую оптимизацию" и "дефрагментацию" смежных "сегментов" помеченных как освобожденные то никакой утечки небудет...

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

Re:

> Вообще не понятно, они там в firefox что, все новые объекты объёмом по несколько байтов создают, что ли?

Наиболее вероятный размер при malloc() у них ~100b., так что снижение MMAP_THRESHOLD там не помогает.

>или они какой-то свой malloc используют?

Под linux PR_*alloc , PR_Free -- просто обертки для *alloc/free из glibc. см. mozilla/nsprpub/pr/src/malloc/prmem.c

anonymous
()
Ответ на: Re: от anonymous

Re:

Понятно, спасибо за информацию. Получается, что просто для линукса firefox оказался неудачно написанной программой...

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

Не, ну так в чём вопрос: это где-нибудь реализовано? или предлагаете писать самому? :-)

mr ★★
() автор топика
Ответ на: Re: от mr

Re:

> Получается, что просто для линукса firefox оказался неудачно написанной программой...

mozilla, seamonkey, netscape 6,7 тоже . Это общий для них прокол.

В prmem.c есть код, реализующий свой аллокатор, можно принудительно собирать с ним, а не с обертками для *alloc/free, но он под linux сбоит из-за threads. Отлаживать не осилил.

Все же посмотреть на openBSDшный аллокатор стОит.

anonymous
()
Ответ на: Re: от anonymous

Re:

ЗЫ: авторы mozilla этим не озабочены, т.к. большинство пользователей mozilla/firefox живет на NT 5.*, в которой аллокатор из стандартной RTL в _данном_ случае возвращает ядру память лучше, чем glibcный, но oчень медленный поэтому.

anonymous
()
Ответ на: Re: от anonymous

Re:

>> Все же посмотреть на openBSDшный аллокатор стОит.

mmap/munmap based???

ИМХО это тоже не панацея...

Ex ★★
()
Ответ на: Re: от anonymous

Re:

Вот здесь написано про этот аллокатор самим Theo: http://marc.theaimsgroup.com/?l=openbsd-misc&m=112475373731469&w=2

Я так понял, в аллокаторе openbsd mmap используется только для "object that is >= 1 page in size". Т.е. преимуществ перед линуксом, не считая вопросов безопасности, нет--в линуксе вроде всё то же самое (при установке соотвествующего MMAP_THRESHOLD). Так что вряд ли поможет.

mr ★★
() автор топика
Ответ на: Re: от mr

Re:

> Я так понял, в аллокаторе openbsd mmap используется только для "object that is >= 1 page in size".

Спасибо, пробежал текст. Неясно, используется ли там sbrk вообще? Если нет, то это хорошо: при malloc(100), например будет все равно сделан mmap(PAGE_SIZE). При следующем malloc(100) будет возвращен адрес из уже mmapнутого куска. Когда все они освободятся, munmap вернет page системе.

anonymous
()
Ответ на: Re: от anonymous

Re:

>Наиболее вероятный размер при malloc() у них ~100b.

Таки нет--я сейчас поэкспериментировал и вот что получилось.

Даже при снижении MMAP_THRESHOLD до 50-25 занимаемый firefox'ом (например, сразу после старта) объём не сильно увеличивается, не более чем в полтора раза ==> в переменных 20-1000 байт там весьма мало содержится (потому как размер страницы getpagesize()=4096 байт). При этом MMAP_THRESHOLD firefox по-прежнему весьма неохотно отдаёт память системе при закрытии окон/табов ==> больших переменных объёмом порядка 1K-1M там нет или практически нет. Объём firefox'а сразу после старта сильно (многократно) возрастает только при понижении MMAP_THRESHOLD до 15. Все вышеозначенные тесты делались просто коммандой типа MALLOC_MMAP_THRESHOLD_=25 MALLOC_MMAP_MAX_=100000000 firefox.

Итак, получается, что средний объём динамических переменных там не более 20 байт! А теперь скажите мне люди, что это, блин, за такой размер 20 байт и это вообще как, нормально? Кто виноват? Что делать?

mr ★★
() автор топика
Ответ на: Re: от mr

Re:

> Что делать?

Прикручивать к mozilla свой аллокатор.

Или пользовать ее под windows 2000, там память таки системе возвращается, что самое абыдное.

anonymous
()
Ответ на: Re: от anonymous

Re:

>Или пользовать ее под offtopic 2000, там память таки системе возвращается, что самое абыдное.

А вот интересно, как там оно реализовано? Дефрагментатор памяти мокросовтовцы там что ли прикрутили, им не впервой? :-)

mr ★★
() автор топика
Ответ на: Re: от mr

Re:

> А вот интересно, как там оно реализовано?

Тоже интересно ;) google пока ничего полезного не находит (disclaimer: в windows я полный ноль).

anonymous
()
Ответ на: Re: от anonymous

Re:

На http://forum.mozilla.ru/viewtopic.php?id=7147&p=1 нашлось что-то, смотри ключевое слово config.trim_on_minimize. Я так понял, что венда при минимизации дефрагментирует память приложения?

P.S. Я и в линуксе-то почти ноль :)

mr ★★
() автор топика

Читаю этот thread и никак не пойму, о чем речь. Какая может быть дефрагментация, прозрачная для приложения?? garbage collecting, естественно, остается за скобками.

В реализации аллокаторов памяти не очень много свободы. Да, бывают аллокаторы, которые лучше работают с маленькими кусками, устраивая для них отдельные арены. Но это почти и все, что можно сделать.

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

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

> Какая может быть дефрагментация, прозрачная для приложения??

Никакой, по крайней мере, в linux.

> бывают аллокаторы, которые лучше работают с маленькими кусками, устраивая для них отдельные арены.

Возможно, offtopic так и делает.

mozilla/firefox/netscape под win2000 память таки отдают системе в ожидаемом кол-ве при закрытии страниц, в отличие от. :(

Чтобы исключить вопросы: firefox-win32 под wine в смысле памяти ведет себя так же, как родной linuxовый.

anonymous
()
Ответ на: Re: от mr

Re:

> Я так понял, что венда при минимизации дефрагментирует память приложения?

Что понимается под дефрагментацией памяти?

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

> firefox-win32 под wine в смысле памяти ведет себя так же, как родной linuxовый.

Интересное наблюдение. Вот еще бы суметь его правильно интерпретировать ...

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

> runtime libraries у них разныe под wine и под windows.

то бишь malloc/free в wine сделаны на mmap/sbrk/munmap/..., а в RTL в windows на HeapAlloc() & friends.

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

> то бишь malloc/free в wine сделаны на mmap/sbrk/munmap/..., а в RTL в windows на HeapAlloc() & friends.

A HeapAlloc со своими friend работает по тому же принципу, что sbrk, т.е. у рантайма есть одна сужаемая/расширяемая область памяти, или система предоставляет отдельную область на каждое обращение к рантайму за памятью?

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

afair, аллокатор в NT заводит несколько 'куч', вообще говоря, произвольно расположенных в физической памяти. (аналог mmapнутых областей). Отдельные списки chunk'ов размером по 8, 16, 32, etc. байт (в разных кучах ???) Пользовательский процесс может явно запрашивать создание отдельной кучи, например, если нужна память для отображения html-страницы. После закрытия страницы можно освободить эту кучу _целиком_, она вернется системе.

ЗЫЖ никогда не работал на windows, слова выше перевраны из MSDN. :)

В mozilla это не используется, там везде используется *alloc/free, которые в RTL NT написаны через вызовы Heap...().

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

Полезная информация. Хотя то что говорилось про windows (да и про STL тоже) я не понял. Да и вообще ничего не понял, если честно :-)

А вопрос был не только про firefox, а более общий. Решена ли в linux как-нибудь/где-нибудь подобная проблема?

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

В некоторых программах используются свои специальные аллокаторы. Например, VM emacs не распухает со временем, VM X-сервера тоже колеблется и в среднем не растет.

Видимо, для mozilla нужен свой аллокатор по типу offtopic.

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

>В некоторых программах используются свои специальные аллокаторы. Например, VM emacs не распухает со временем, VM X-сервера тоже колеблется и в среднем не растет.

Так emacs это ж lisp вроде бы в этом языке таких проблем быть не может, по крайней мере не должно. Т.е. можно сказать, "правильный" аллокатор уже в сам интерпретатор языка lisp встроен. Я это всё к тому, что не слишком сложно ли сложно там это реализовано. Всё таки то пользовательская программа, а то интерпретатор языка (исходники его ещё не видел). Я ж не собираюсь тут же писать аллокатор для firefox, мне сначала разобраться в вопросе хотя бы. Если ничего проще нет--то что поделаешь, надо так надо :-)

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

Про lisp не знаю.

Если бы было простое решение для mozilla/ff (и для остальных, konqueror и opera так же пухнут) под linux, то его бы уже опубликовали.

Прикрутить спец. аллокатор в любом случае стОит.

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

Нашёл тут ещё одну кратенькую статью по теме (которая почему-то удалена с tldp.org "removed for review"): http://www.faqs.org/docs/Linux-HOWTO/C++Programming-HOWTO.html#s9 Да, что такое garbage collector мне надо разобраться, я про них только краем ухом касательно java и lisp слышал. Ладно, всем спасибо за информацию...

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

Не, а это серьёзно загадка для меня :) Это ж операторы => их реализация должна в компиляторе g++ быть. А для C всё в glibc сидит. Если логически рассуждать. Ладно, не томи, говори разгадку.

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

Ещё раз всем спасибо за информацию :)

А вот ещё такой "детский" вопрос у меня: а есть такой язык (пригодный для разных областей прикладного программирования), который бы не имел заморочек C/C++, в том числе по части управления памятью, и при этом был не намного медленнее C/C++? Или это фантастика?

mr ★★
() автор топика

Итак, господа, подведём итог. "Прозрачная для приложения дефрагментация" в C/C++ невозможна, средств возвратить память системе кроме использования mmap в linux нет (а минимальный выделяемый размер mmap 4096 байт). Итак, ситуация безнадёжная (по крайней мере, мне не понятно как тогда можно написать "свой аллокатор" для firefox).

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

> средств возвратить память системе кроме использования mmap в linux нет

Еще sbrk(-some_size)

> мне не понятно как тогда можно написать "свой аллокатор" для firefox

Иметь много mmapнутых областей размером по несколько страниц, которые немедленно munmap'ить при free(), если область полностью освободилась?

Под offtopic как-то это делается, код mozilla тот же самый (см. выше про firefox-win32 под wine!). Это вопрос к malloc/free из runtime.

ЗЫ желающие могут подтвердить/проголосовать этот баг на bugzilla.mozilla.org, ссылка выше. Чем больше там будет votes, тем быстрее moz. developers зашевелятся.

anonymous
()

$ valgrind --tool=memcheck  -v --leak-check=yes --show-reachable=yes --leak-resolution=high /usr/lib/mozilla-firefox/firefox-bin  

...[Skipped]...



==6386== LEAK SUMMARY:
==6386==    definitely lost: 29,611 bytes in 335 blocks.
==6386==    indirectly lost: 102,934 bytes in 1,072 blocks.
==6386==      possibly lost: 1,219,851 bytes in 19,815 blocks.
==6386==    still reachable: 11,338,626 bytes in 100,211 blocks.
==6386==         suppressed: 0 bytes in 0 blocks.
--6386--  memcheck: sanity checks: 24494 cheap, 980 expensive
--6386--  memcheck: auxmaps: 0 auxmap entries (0k, 0M) in use
--6386--  memcheck: auxmaps: 0 searches, 0 comparisons
--6386--  memcheck: secondaries: 920 issued (58880k, 57M)
--6386--  memcheck: secondaries: 2711 accessible and distinguished (173504k, 169M)
--6386--     tt/tc: 17,212,814 tt lookups requiring 308,544,749 probes
--6386--     tt/tc: 17,212,814 fast-cache updates, 18 flushes
--6386-- translate: new        235,350 (5,567,728 -> 99,076,280; ratio 177:10) [0 scs]
--6386-- translate: dumped     0 (0 -> ??)
--6386-- translate: discarded  1,266 (28,350 -> ??)
--6386-- scheduler: 1,173,430,392 jumps (bb entries).
--6386-- scheduler: 24,494/19,177,597 major/minor sched events.
--6386--    sanity: 24495 cheap, 980 expensive checks.
--6386--    exectx: 30,011 lists, 60,493 contexts (avg 2 per list)
--6386--    exectx: 2,307,814 searches, 6,657,003 full compares (2,884 per 1000)
--6386--    exectx: 91,702 cmp2, 11,652 cmp4, 468,710,962 cmpAll

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

>а минимальный выделяемый размер mmap 4096 байт

В винде тоже.

Ядро может мэппить программе только 0x1000 байт.

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

>Иметь много mmapнутых областей размером по несколько страниц, которые немедленно munmap'ить при free(), если область полностью освободилась?

Так в том-то и дело, что область в фаерфоксе вряд ли будет полностью освобождаться :(

>Под offtopic как-то это делается, код mozilla тот же самый (см. выше про firefox-win32 под wine!). Это вопрос к malloc/free из runtime.

Ну да, магические слова типа "HeapAlloc" произносятся, а что это такое не понятно. Погуглить что ли...

>ЗЫ желающие могут подтвердить/проголосовать этот баг на bugzilla.mozilla.org, ссылка выше. Чем больше там будет votes, тем быстрее moz. developers зашевелятся.

Проголосовал. Только там почему-то очень мало голосов. Может это дублирующий тред?

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

> Может это дублирующий тред?

Там несколько багов на эту тему, причем не все про linux.

https://bugzilla.mozilla.org/query.cgi

https://bugzilla.mozilla.org/show_bug.cgi?id=29285 https://bugzilla.mozilla.org/show_bug.cgi?id=319143 https://bugzilla.mozilla.org/show_bug.cgi?id=276342 https://bugzilla.mozilla.org/show_bug.cgi?id=268138

> Только там почему-то очень мало голосов.

Тогда эту тему надо поднять наверх в Talks. Разговоры про неотдачу памяти браузерами под linux там периодически всплывают.

Голосовать стоит за баг из предыдущего поста, т.к. в нем есть вывод malloc_stats(), надо еще сменить в том баге OS=Windows2000 на OS=All.

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

Меня мучают смутные сомнения о том, что разработчики firefox будут что-то делать.

Я вот подумал: может openbsd установить? Чтобы проверить, как там всё обстоит с аллокатором (хотя надежды мало).

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

google говорит, OpenBSD 3.5 (спец. image???) вроде работала.

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

В общем, раскопал в локалке и таки установил я себе эту openbsd. Мне понравилось :) Ну и firefox из репозитария поставил, конечно.

Собственно, по делу. Сразу после запуска тамошний top показывает для фаера SIZE 11M RES 28M. Памяти firefox кушает вообще как-то умеренно: открыл я несколько окон и несколько табов, в каждом из них история по несколько пунктов (открытые в случайном порядке), и top показывает SIZE 46M RES 60M, что для firefox'а совсем не плохо. При этом память во время работы firefox в отличие от линукса, не только может увеличиваться, но и время от времени уменьшаться. Ну и самое главное: при закрытии всех окон/табов кроме одного, использование памяти сразу сокращается, но правда только до SIZE 30M RES 41M. А по скорости разницы не заметно. В общем, надо его ещё потестировать, но отличия видны не вооружённым взглядом.

Использовалось: openbsd 3.8, firefox 1.0.6p1 в сборке на gtk1.2. Ну и программку тестовую надо попробовать, само собой...

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

A browser.*.cache.enable ставил в false ?

Программку можно такую:

#include <stdlib.h>
#include <stdio.h>

#define N (1024*256)
#define SIZE (1024)

char *p [N];

int main (void) {
int n, i;
for (n = 0; n < N; n++) {
if ((p[n] = malloc(sizeof(char)*SIZE)) == NULL)
break;
for (i = 0; i < SIZE; i++)
p[n][i] = '\0';
if (n % 1024 == 0)
printf("%d kB скушали \n", n+1);
}
for (i=0; i < n; i++) {
free((void*)p[i]);
if (i % 1024 == 0) {
printf("%d kB освободили. Проверь VM\n",i+1);
getchar();
}
}
return 0;
}

Т.к. free() делаем не с конца и SIZE=1024 < MMAP_THRESHOLD, то под linux она не отдает память до освобождения n-го блока.

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

mr, спасибо, порадовал. Возможно openbsd-шный malloc можно собрать под linux.

btw, в OpenBSD malloc.c изменился с 3.8 (см. их CVS).

Имхо, надо эти опыты кратко изложить в отдельной теме, т.к. эта уже уплыла вниз. Возьмешся? :)

Мужыки-та не знають!

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

> Возможно openbsd-шный malloc можно собрать под linux.

Ядру не поплохеет от немеряного кол-ва mmap() ?

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