LINUX.ORG.RU

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


0

0

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

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

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

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

★★

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

Ну вот, поставил я его компилится (версия 1.0.8, более новая не скомпилилась). Пока что не вылетел, так что есть шанс. Только в тормозной openbsd всё это, чувствую, будет очень долго происходить...

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

хе-хе, дык C++ ;)

в -current портах есть последний. для того, чтобы поставить -current
порты:

 # cd /usr/ports
 # cvs -z3 -fqd anoncvs@anoncvs.ca.openbsd.org:/cvs up -PAd .

установить pkg_* от -current:

 # cd /usr/src/usr.sbin/pkg_add
 # cvs -z3 -fqd anoncvs@anoncvs.ca.openbsd.org:/cvs up -PAd .
 # make obj
 # make depend
 # make
 # make install

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

> в -current портах есть последний.

не надо -current.

Подождем, достаточно поиметь proof of concept в виде сравнения вывода malloc_stats() под OpenBSD и Linux.

-- другой анонимус

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

firefox 1.5.0.1 я пытался установить так:

# cd /usr/ports/www/mozilla-firefox
# cvs -z3 -fqd anoncvs@anoncvs.ca.openbsd.org:/cvs up -PAdrOPENBSD_3_9 .
# make install clean CLEANDEPENDS=Yes

Оно мне выдало, что есть зависимость от cairo, а этой самой cairo в текущих пакетах нет (а пакеты openbsd 3.9 ещё не появились).

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

Так, а где должен быть вывод malloc_stats()? Что-то в выводе я его не вижу (запускал просто командой firefox&).

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

Что-то не появляется. Слушай, а почему ты решил, что в openbsd есть функция malloc_stats()?

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

похоже, через trace-malloc malloc_stats() она не выводит.

mr, если не очень устал еще, в nsprpub/pr/src/malloc/prmem.c вставь malloc_stats() прямо в PR_Free():

PR_IMPLEMENT(void) PR_Free(void *ptr)
{
fprintf(stderr,"DEBUG:PR_Free:"); malloc_stats();
if (use_zone_allocator)
pr_ZoneFree(ptr);
else
free(ptr);
}

Для bugzilla так делал.

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

в openbsd можно использовать "опции malloc":
 http://www.openbsd.org/cgi-bin/man.cgi?query=malloc&sektion=3

 % export MALLOC_OPTIONS=D
 % firefox &

надо только пересобрать libc с -DMALLOC_STATS.
ну или в файле /usr/src/lib/libc/stdlib/malloc.c:

заменить:

#ifndef MALLOC_STATS
#undef  MALLOC_STATS
#endif

на:

#defined MALLOC_STATS

и пересобрать libc:

 # cd /usr/src/lib/libc
 # make obj
 # make depend
 # make
 # make install

(не сильно долгий процесс..)

cu.

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

Из man malloc:

Malloc will first look for a symbolic link called /etc/malloc.conf and
next check the environment for a variable called MALLOC_OPTIONS and fi-
nally for the global variable malloc_options and scan them for flags in
that order. Flags are single letters, uppercase means on, lowercase
means off.

A ``Abort''. malloc() will coredump the process, rather than tol-
erate failure. This is a very handy debugging aid, since the
core file will represent the time of failure, rather than when
the null pointer was accessed.

D ``Dump''. malloc() will dump statistics in a file called
malloc.out at exit. This option requires the library to have
been compiled with -DMALLOC_STATS in order to have any effect.
Так, как это расшифровать?

>man malloc_dump посмотри, возможно, она так называется
$ locate malloc|grep man
/usr/share/man/cat3/malloc.0
/usr/share/man/cat5/malloc.conf.0
/usr/share/man/cat9/malloc.0
/usr/share/man/cat9/uvm_km_kmemalloc.0

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

> man malloc_dump

просто man malloc, где говорится о переменной MALLOC_STATS. Для этого надо собрать libc с -DMALLOC_STATS, см, пост предыдущего автора.

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

Так, в портах нету, где брать не понятно, и самое главное как её безопасно установить тоже непонятно.

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

> как её безопасно установить тоже непонятно.

LD_PRELOAD там есть?

> Так, в портах нету, где брать не понятно,

Погоди, "ишшы, должон быть" (c) :)

Минуту.

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

> Постелька зовёт меня :(

Господа гусары, папрашу промолчать-с (c) :)

У меня сейчас дерево /usr/src checkout'ится, завтра посмотрим.

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

Я скопировал malloc.c и thread_private.h, компилирую gcc -DMALLOC_STATS -shared -fpic malloc.c -o malloc.so (когда без -DMALLOC_STATS файл .so другой)

Когда запускаю с LD_PRELOAD=./malloc.so ls всё нормально, когда с MALLOC_OPTIONS=D LD_PRELOAD=./malloc.so ls выводит сообщение: ls in malloc(): warning: unknown char in MALLOC_OPTIONS

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

Так вот почему не работает: LD_PRELOAD=./malloc.so не действует!

Заменил в malloc.c строку

wrtwarning("unknown char in MALLOC_OPTIONS");

на

wrtwarning("unknown hop! hop! char in MALLOC_OPTIONS");

скомпилил в .so: на выводе всё те же

unknown char in MALLOC_OPTIONS

Как это объясните, господа?

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

Да, firefox не ругается на это, вот вывод после установки соотв. LD_PRELOAD и MALLOC_OPTIONS:
$ firefox& 
[1] 17421
sh in malloc(): warning: unknown char in MALLOC_OPTIONS
$ malloc() warning: hop! hop! Couldn't dump stats
malloc() warning: hop! hop! Couldn't dump stats
pwd in malloc(): warning: unknown char in MALLOC_OPTIONS
expr in malloc(): warning: unknown char in MALLOC_OPTIONS
malloc() warning: hop! hop! Couldn't dump stats
malloc() warning: hop! hop! Couldn't dump stats
sh in malloc(): warning: unknown char in MALLOC_OPTIONS
malloc() warning: hop! hop! Couldn't dump stats
malloc() warning: hop! hop! Couldn't dump stats

Где hop! hop! -- это опять я добавил.

Вот место из malloc.c с Couldn't:

#ifdef MALLOC_STATS
static void
malloc_exit(void)
{
        char    *q = "malloc() warning: hop! hop! Couldn't dump stats\n";
        int     save_errno = errno, fd;
 
        fd = open("malloc.out", O_RDWR|O_APPEND);
        if (fd != -1) {
                malloc_dump(fd);
                close(fd);  
        } else
                write(STDERR_FILENO, q, strlen(q));
        errno = save_errno;
}
#endif /* MALLOC_STATS */

Теперь надо понять, почему он не хочет открывать malloc.out.

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

Переправил я поток на stderr, заменив fd=... на fd = open("/dev/stderr", O_RDWR|O_APPEND);

При запуске и закрытии firefox получаю кучу сообщений типа:

8b09b000 569499 0x7ca48060 0 (of 8) x 512 @ 0x8ac93000 --> 0x0
8b09c000 569500 .. 569883 not mine
8b21c000 569884 0x88b790e0 0 (of 8) x 512 @ 0x8ae14000 --> 0x0
8b21d000 569885 .. 570179 not mine
8bb3c000 572220 .. 572824 not mine
8bd99000 572825 .. 572825 in use
8bd9a000 572826 .. 572917 not mine

Которая заканчивается чем-то типа:

Minsize 16
Maxsize 2048
Pagesize        4096
Pageshift       12
In use  16384
Guarded 0

Как это относится к нашим целям непонятно. Похоже прокольчик вышел?

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

> почему он не хочет открывать malloc.out.

т.к. O_RDWR|O_APPEND, malloc.out должен существовать до open()

touch ./malloc.out :)

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

> Как это относится к нашим целям непонятно. Похоже прокольчик вышел?

Нет прокола. Задачка решена уже в принципе.

Осталось научится прочесть из дампа кол-во mallocнутого, и количество mmapнутого. Если (mallocнутое)/(mmapнутое) в openbsd > (total in use bytes)/(total system bytes) в таком же сеансе под linux, то эффект от mmap-ного malloc четкий.

Сравнивать с https://bugzilla.mozilla.org/show_bug.cgi?id=324081#c17

К вечеру разгребусь с делами, посмотрю на исходник malloc_dump, возможно его надо поправить для вывода нужных данных.

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

Честно говоря, я пока не понял, что там вообще в дампе выводится. Но на количество malloc'ов это не похоже (вроде их должно быть гораздо больше, чем ~1000 строк в malloc.out). Хотя может что и получится.

P.S. Про touch ./malloc.out верно подмечено. Кстати, вас двое (анонимусов) или один? :)

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

> Кстати, вас двое (анонимусов) или один? :)

анонимус един в многих лицах (c) :)))

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

> Но на количество malloc'ов это не похоже

там не кол-во вызовов malloc/free надо, a сколько байт они запросили-освободили (1) . И сравнить, сколько байт в то же время mmapнуто (2).

В одинаковом сеансе отношение (1)/(2) должно быть больше, чем (used bytes)/(system bytes) из вывода malloc_stats() в linux.

Например, после закрытия всех табов в твоем примере выше в openbsd (1)/(2) ~ 10M/20M (VM упало до ~40M c 60M), а в linux (used bytes)/(system bytes) ~ 10M/40M, т.к. там отожранные 60М не возвращаются ядру.

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

Да не, я про то, как извлечь эту информацию...

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

Слушай, может тогда не париться с этим дампом, а так просто и написать сколько потребляется памяти?

Типа в openbsd3.8 сразу после запуска с пустой страницей фаер отжирает 25M (ил 29M с каким-нибудь небольшим сайтиком). После нескольких часов активной работы и массы открытых окон и табов объём достигает 90M. После закрытия всех окон и табов, кроме одного пустого, объём уменьшается до 56M.

На английский перевести и опубликовать ?

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

Опубликовать... Как красиво звучит.

Только надо открывать ~30 табов с bbc для соответствия с той темой из bugzilla.

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

Вывод ps vp `pidof firefox-bin` в openbsd и linux сравнивать напрямую нельзя. Для bugzilla нужна статистика самогО аллокатора по динамической памяти.

С malloc_dump() сегодня не успеваю. Разберусь после разгреба работы.

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

------- Comment #25 From Boris Zbarsky 2004-09-21 22:50 PDT [reply] -------

The point is, we don't want to be in the business of rewriting the OS allocators...

Они всегда так говорят (вот был, например, баг в gtk с фокусом в некоторых wm--они даже не пошевелились, чтобы исправить, так ведь в конце концов его сотрудники РедХат исправили). Вряд ли мозилловцы будут что-то предпринимать в этом направлении.

С таки запросами можно сразу к маинтайнерам glibc идти. Хотя и те могут сказать то же самое. Самим что ли пытаться переписать этот аллокатор?

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

> The point is, we don't want to be in the business of rewriting the OS allocators...

Следующий пост, по памяти : " .. memory management is not the most complicated part of the system mozilla successfully replaces ... "

В glibc не нужно, ее аллокатором большинство программ довольно.

Не можешь огранизовать доступ к OpenBSD по ssh для одного IP? так дело пойдет быстрее.

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

>Не можешь огранизовать доступ к OpenBSD по ssh для одного IP? так дело пойдет быстрее.

Не, чё-то не знаю, как это сделать. Он из под локального ip впускает (с 192.168.x.x), а из-под внешнего нет. Может настроить sshd как-то надо, не знаешь как?

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

Afaiu, настраивается не сам sshd, а аналог линуксового tcpd.

См. /etc/hosts.allow, /etc/hosts.deny.

В hosts.deny там вероятно ALL: ALL, в hosts.allow надо вписать что-то вроде in.sshd: <внешний адрес> .

man hosts.allow ?

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

Не, в hosts.deny и в hosts.allow ничего нет.

А вообще знаешь, а может быть так, что у меня нет внешнего ip? Типа может мой провайдер предоставляет мне доступ в интернет через какой-нибудь маскарадинг?

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

>Типа может мой провайдер предоставляет мне доступ в интернет через какой-нибудь маскарадинг?

А вообще знаешь, видимо так и есть, потому что "... password" откликается даже когда у меня sshd выключен. Что же тогда делать?

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

О, новое открытие (благодаря http://live.gnome.org/MemoryReduction/NewAlloc). Оказывается, есть функция madvise, которая может говорить ядру освободить страницы памяти.

Посмотрите на такой пример simple.c: #include <malloc.h> #include <stdio.h> #include <sys/mman.h> #include <errno.h>

int ** p; const int N=200000; const int number=50;

int main(int argc, char *argv[]) { int i; p=(int**)malloc(N*sizeof(int*)); for(i=0;i<N;i++) { p[i]=(int*)malloc(number*sizeof(int)); p[i][0]=1; } printf("Array size is %d bytes\n",N*number*sizeof(int)+N*sizeof(int*)); malloc_stats(); for(i=0;i<N-1;i+=1) { free(p[i]); } malloc_stats(); sleep(10); char str[80]; sprintf(str,"%d",p[82]); int address=atoi(str); int block_start=(address/4096)*4096; int length=((N-84)*number*sizeof(int)/4096)*4096; madvise(block_start,length,MADV_DONTNEED); printf("\nerrno=%d\n\n",errno); for(i=1;;i++) { sleep(5); malloc_stats(); } return 0; }

Хотя malloc_stats показывает одно и тоже, командой top или ps -eo command,rss|grep ./simple|grep -v grep можно узнать, что память освободилась! А ведь, пожалуй, это чрезвычайно упрощает задачу -- надо просто подправить старый алокатор!

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

Форматирование, блин...

simple.c:

#include <malloc.h>
#include <stdio.h>
#include <sys/mman.h>
#include <errno.h>

int ** p;
const int N=200000;
const int number=50;

int main(int argc, char *argv[])
{
  int i;
  p=(int**)malloc(N*sizeof(int*));
  for(i=0;i<N;i++)
  {
    p[i]=(int*)malloc(number*sizeof(int));
    p[i][0]=1;
  }
  printf("Array size is %d bytes\n",N*number*sizeof(int)+N*sizeof(int*));
  malloc_stats();
  for(i=0;i<N-1;i+=1)
  {
    free(p[i]);
  }
  malloc_stats();
  sleep(10);
  char str[80];
  sprintf(str,"%d",p[82]);
  int address=atoi(str);
  int block_start=(address/4096)*4096;
  int length=((N-84)*number*sizeof(int)/4096)*4096;
  madvise(block_start,length,MADV_DONTNEED);
  printf("\nerrno=%d\n\n",errno);
  for(i=1;;i++)
  {
  sleep(5);
  malloc_stats();
  }
  return 0;
}

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