nixp.ru v3.0

23 мая 2017,
вторник,
00:28:49 MSK

DevOps с компанией «Флант»
yads написал 27 мая 2008 года в 17:12 (432 просмотра) Ведет себя неопределенно; открыл 6 тем в форуме, оставил 36 комментариев на сайте.

На Си написано приложение регулярно использующее malloс, realloc и free. По ходу работы размер приложения в памяти постоянно растет, как будто malloс и realloc работают нормально, а free только когда захочет. Почему так происходит и надо ли что-то с этим делать?

metal

Надо, запустить его под valgrind и устранить утечки памяти, если они есть.

yads

Запустил, получил много ругательств на все используемые библиотеки, но после этого выполнение прекратилось, а потерь памяти обнаружено не было. Valgrind умеет обращаться с демонами? (память теряется во время работы, а не при запуске)

metal

valgrind умеет с любыми программами работать. С какими параметрами запускал? «Definitely lost» есть?

yads

# valgrind --leak-check=full --leak-resolution=high --undef-value-errors=yes --show-reachable=yes

……..

==1666== LEAK SUMMARY:

==1666== definitely lost: 0 bytes in 0 blocks.

==1666== possibly lost: 0 bytes in 0 blocks.

==1666== still reachable: 24,896 bytes in 8 blocks.

==1666== suppressed: 0 bytes in 0 blocks.

metal

Хороший результат. Давай тогда к 1-му посту, как мерил? И что подразумевается под постоянно растет?

yads

Смотрю поля VSZ,RSS в выдаче ps aux. Их значения увеличиваются.

metal

Это еще не говорит о утечках. Утечки если они увеличиваются бесконечно.

yads

Ну бесконечно не получится — когда-то произойдет перезагрузка и все начнется с начала, но за 2 дня работы выросло с 30Mb до 300Mb и продолжает также расти дальше.

myst

А вот это уже симптом… Попробуй собрать с каким-нибудь ElectricFence ещё и посмотри, что получится.

yads

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

myst

На самом деле ни один (кроме OpenBSD’шного) free не отдаёт память назад ядру. Он её повторно использует в следующих malloc’ах. Т.е. такое, если ты запрашиваешь n, отдаёшь n (память просто помечается как свободная, но ядру не отдаётся), а потом запрашиваешь n+1, то уже запрошенной памяти не хватит, и malloc попросит у ядра ещё.

Чтобы запросов не было много, обычно используется такой алгоритм: каждый раз, когда требуется память запрашивается столько, сколько уже запрошено или, если нужно, больше. Т.е.

1. Выделено 0, запрашивается 100 => 100

2. Выделено 100, запрашивается 10 => 200

3. Выделено 200, запрашивается 1000 => 1200

4. Выделено 1200, запрашивается 10 => 2400

Возможно при каком-то хитром стечении обстоятельств эта схема может работать плохо. Но скорее всего — баг в malloc/free. Под чем запускаешь свою прогу?

yads

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

Сервер работает под Debian-linux, ядро 2.6.18.

metal

Для больших кусков памяти используется mmap, так что такого эффекта быть не должно. Точнее, он не должен быть значительным.

myst

Если ты постоянно запрашиваешь больше, чем отдаёшь, то память будет рости — это понятно. Проблема в том, что при хитрых запросах, память будет рости непропорционально запросам.

yads

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

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

myst

Видимо всё-таки баг в glibc… Хотя это ооочень маловероятно. А попробуй собрать с, например, jemalloc.

yads

C jemalloc не разобрался, но пока искал ее нашел функцию alloca. Проверил — похоже с ней размер приложения не растет. Только не понял получу ли я какие-то другие проблемы при использовании alloca вместо malloc или нет.

Прблемы есть — память быстро кончается…

yads

Проблема решена. Надо использовать malloc, а после free вызывать malloc_trim и освобожденная память будет возвращена ядру.

sas

Ваше решение не верно. Посмотрите на http://www.linuxdevcenter.com/lpt/a/6808

yads

Мои знания английского недостаточны чтобы из такой статьи понять в чем я ошибся.

Не ли информации на русском?

myst
yads
C jemalloc не разобрался, но пока искал ее нашел функцию alloca. Проверил — похоже с ней размер приложения не растет. Только не понял получу ли я какие-то другие проблемы при использовании alloca вместо malloc или нет.

Прблемы есть — память быстро кончается…

alloca выделяет память на стеке. Стек, понятное дело, резиновый, но не настолько чтобы уж совсем.

metal


#include
#include
#include
#include
#include
#define OUTSTEP 0x00040000
void mem_usage(const char* path)
{
  char string[40];
  FILE* file = fopen(path,"r");
  if(!file)
  {
    fprintf(stderr, "fail open file:%s", path);
    return;
  }
  while(fgets(string, 40, file))
  {
    if(!strncmp(string, "Vm", 2))
    {
      fputs(string, stdout);
    }
  }
  fclose(file);
}
int main(int argc, char* argv[])
{
  int max_size, count_out, iteration, i=0, limit = 0, j=0;
  pid_t mypid;
  char path[24];
  if(argc>1)
  {
    max_size = atoi(argv[1]) * 1024;
  }
  else
  {
    printf("usage: testmalloc max_alloc in KB\n");
    _exit(1);
  }
  mypid = getpid();
  sprintf(path, "/proc/%d/status", mypid);
  count_out = max_size/OUTSTEP + 1;
  iteration = 0;
  do
  {
    fprintf(stdout, "iteration %d\nbefore:\n", iteration);
    mem_usage(path);
    if(max_size - limit > OUTSTEP)
    {
      limit += OUTSTEP;
    }
    else
    {
      limit = max_size;
    }
    for(;j
    {
      void* pointer = malloc(j);
      if(!pointer)
      {
        fprintf(stderr, "failed alloc %d bytes\n");
        mem_usage(path);
        _exit(1);
      }
      memset(pointer, j, j);
      free(pointer);
    }
    iteration++;
    fputs("\nafter:\n",stdout);
    mem_usage(path);
  }while(++i
  return 0;
}

У меня нет никаких негативных эффектов.

yads

Не понял какие негативные эффекты проверялись?

metal
yads
Не понял какие негативные эффекты проверялись?

Как ты описываешь, постоянный рост VM.