Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Out of memory после полумесяца работы программы.


Страницы:   1 2 3

Дмитрий Белькевич ©   (2011-06-07 10:46:00) 

Утечек памяти нет (судя по эврикалог). Out of memory случается где-то на 10-15 день работы приложения. Случается при попытке выделения памяти под динамический массив:

SetLength(FBuff16, AllocSLiceSz div 2);

AllocSLiceSz равен 31768000, т.е. выделяется около 31 мб.

Использую менеджер памяти FastMM.

Вопросов, как обычно, два - кто виноват и что делать...


Sha   (2011-06-08 14:20:00) [21]

> выделяю по сто или по 1000 элементов за раз, позже обрезаю до нужного размера.

Так тоже не всегда хорошо делать, вот пример (облом на 2078 проходе).

procedure TForm1.Button1Click(Sender: TObject);
var
s: string;
a: array of string;
i, count: integer;
begin;
i:=0;
count:=4000;
try
  SetLength(a, 2*count);
  while i<count do begin;
    inc(i);

    //выделяем
    SetLength(s, 1000*1000);
    SetLength(a[i*2-2], 1);

    //работаем
    SetLength(s, 1);
    a[i*2-1]:=s;

    //освобождаем
    SetLength(s, 0);
    end;
  SetLength(a, 0);
except
  Memo1.Lines.Add(IntToStr(i));
  end;
end;


KSergey   (2011-06-08 13:43:00) [22]

У меня есть дурацкий (не подходящий для этого случая, увы) рецепт: переходить на .NET - тама дефрагментация .NET-ядром делается периодически, волшебно или на ручной тяге.
Ну или подобным образом переписывать свою прогу.
Вот только это нереально, увы.

PS
У нас сервер всего на работу в течение дня рассчитан, и то частенько не доживает :( Бороться трудно


sniknik   (2011-06-08 09:42:00) [23]

> Простого рецепта, типа "освободи, когда станет ненужным" не существует.
а у меня это не рецепт, это правило (еще с паскаля). которое судя по всему сильно снижает вероятность подобных эксцессов.

> позже обрезаю до нужного размера.
зачем? тут ИМХО тоже кроется возможность фрагментации, ну типа выделили 1000 после еще байт 10 в другом массиве, у после обрезали первый до 20 байт... куда денутся оставшиеся 980?
и пусть даже следом/в другом цикле  после наслоение следующих выделений будет очистка массивов 10 и 20 то будет ли перераспределение на "потерянные"  980???


Anatoly Podgoretsky   (2011-06-08 09:00:00) [24]


> Всего этого придерживаюсь. Когда массивам записей SetLength
> делаю - выделяю по сто или по 1000 элементов за раз, позже
> обрезаю до нужного размера.
>
> Но вот - всё равно вылазит Out of memory.

Соотвественно ты уже не сможешь выделить тот же блок на 1000 элементов, а только новый, вот так и получает фрагментация и следствие


Sha   (2011-06-08 08:59:00) [25]

... откуда берется фрагментация.


Sha   (2011-06-08 08:58:00) [26]

> sniknik ©   (08.06.11 07:58) [20]

Мой пример просто показывает откуда берется дефрагментация.
Простого рецепта, типа "освободи, когда станет ненужным" не существует.
Представь себе, что в моем примере массив анужен на протяжении всего времени работы. Он занимает не более 4К всего.

procedure TForm1.Button1Click(Sender: TObject);
var
 s: string;
 a: array of string;
 i: integer;
begin;
 i:=0;
 try
   while true do begin;
     inc(i);
     SetLength(s,(1000+i)*1000);
     SetLength(a,i);
     SetLength(a[i-1],1);
     //тут работаем
     SetLength(s, 0);
     end;
 except
   Memo1.Lines.Add(IntToStr(i));
   end;
 end;

Теперь ломаемся на 1563 проходе цикла.


Дмитрий Белькевич   (2011-06-08 08:38:00) [27]


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


Всего этого придерживаюсь. Когда массивам записей SetLength делаю - выделяю по сто или по 1000 элементов за раз, позже обрезаю до нужного размера.

Но вот - всё равно вылазит Out of memory.

Интересна именно дефрагментация памяти. В FastMM готового или не нашел или плохо искал. Не представляю даже, что делать, кроме рестарта. Рыть менеджер памяти самому - как-то не хочется...


sniknik   (2011-06-08 07:58:00) [28]

> Реально ли это исправить, кроме как рестартом?
всегда обнулял в финале, если выделял, после использования, проблем не было (по аналогии раз выделил освободи)

тут, типа -
procedure TForm1.Button1Click(Sender: TObject);
var
s: string;
a: array of string;
i: integer;
begin;
i:=0;
try
  while true do begin;
    inc(i);
    SetLength(s,(1000+i)*1000);
    SetLength(a,i);
    SetLength(a[i-1],1);
    SetLength(s, 0);
    SetLength(a, 0);
    end;
except
  Memo1.Lines.Add(IntToStr(i));
  end;
end;


Sha   (2011-06-08 01:24:00) [29]

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


Sha   (2011-06-08 01:18:00) [30]

> Реально ли это исправить, кроме как рестартом?

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


Дмитрий Белькевич   (2011-06-08 00:10:00) [31]

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


> Вот такая процедура вылетает у меня (D7 без FastMM) на 1543
> проходе цикла,


Подтверждаю. Реально ли это исправить, кроме как рестартом?


Дмитрий Белькевич   (2011-06-08 00:02:00) [32]


> может зря на память "гонишь", может что-то в логике? (вряд
> ли эврикалог/... отследит), ну к примеру потоки не завершаются
> (не падают/выходят без очистки/..., а именно просто впустую
> крутятся, аля зомби поток...)


А я вижу картину падения в эврикалоге. Увы - ничего крамольного.

Полный кусок кода:


    try
     SetLength(FBuff16, AllocSLiceSz div 2);
    except
     on E: Exception do
     begin
      StandardEurekaError(ansistring(Format('Alloc memory raise an exception ! %s. Alloc Size: %d.', [E.Message, AllocSLiceSz])));
      Halt(0);
     end;
    end;


Так вот - эврика успешно создаёт лог, ничего особенного в нём нет.


Sha   (2011-06-07 20:29:00) [33]

Вот такая процедура вылетает у меня (D7 без FastMM) на 1543 проходе цикла,
т.е. когда реально используется всего 2.5М памяти.

procedure TForm1.Button1Click(Sender: TObject);
var
 s: string;
 a: array of string;
 i: integer;
begin;
 i:=0;
 try
   while true do begin;
     inc(i);
     SetLength(s,(1000+i)*1000);
     SetLength(a,i);
     SetLength(a[i-1],1);
     end;
 except
   Memo1.Lines.Add(IntToStr(i));
   end;
 end;


_Юрий   (2011-06-07 20:28:00) [34]

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


_Юрий   (2011-06-07 20:25:00) [35]

У меня ровно тоже самое:
COM - сервер, утечек нет, судя по показаниям FastMM
А вылет с OutOfMemory где-то раз-два в месяц.


sniknik   (2011-06-07 16:19:00) [36]

может зря на память "гонишь", может что-то в логике? (вряд ли эврикалог/... отследит), ну к примеру потоки не завершаются (не падают/выходят без очистки/..., а именно просто впустую крутятся, аля зомби поток...)


Дмитрий Белькевич   (2011-06-07 16:10:00) [37]

Попробовал тестово повыделять память:


procedure TForm19.Button1Click(Sender: TObject);
var
t: array of Byte;
begin
i := 0;
repeat
 if Random(2) = 1 then
  SetLength(f[Random(999)], random(1000000))
 else
  SetLength(f[Random(999)], 0);
 Label1.Caption := IntToStr(i);
 Inc(i);
 SetLength(t, 32000000);
 SetLength(t, 0);
 Application.ProcessMessages;
until False;
end;


второй час - полет нормальный. Пока не представляю, как глюк повторить - не 10 же дней ждать :)


Sha   (2011-06-07 16:06:00) [38]

> Дмитрий Белькевич   (07.06.11 14:24) [8]
> И там далеко не один этот массив используется - что бы вот так -
> сделать статическим - и решить проблему.
> Всякого разного динамического там много.

Можно организовать повторное использование только больших кусков.


RWolf   (2011-06-07 14:40:00) [39]

Если буферы выделять средствами операционки (т.е. HeapAlloc), можно время от времени делать HeapCompact; по идее, это должно убирать фрагментацию памяти процесса.


Дмитрий Белькевич   (2011-06-07 14:24:00) [40]

Приложение - довольно массивный сервер, делающий много всякого разного - раздающий веб, работающий с десятками потоков. И там далеко не один этот массив используется - что бы вот так - сделать статическим - и решить проблему. Всякого разного динамического там много.
Я тоже думаю на фрагментацию памяти. Потому как валится именно после того, как долго поработают. Пробовали несколько раз перезапускать после трех-четырех дней работы - тогда работает нормально, до какого-то времени - дней 12-15, как писал.
Но всё таки удивительно - неужели реаллоки памяти могут так сильно её фрагментировать, что 32-х мегабайтный сплошной фрагмент найти невозможно?
Еще вопрос - можно ли фастмму как-то сказать, что бы он дефрагментировал память? Можно ли что-то кроме перезапуска придумать? Предполагается, что сервер доступен постоянно (насколько железо позволяет, конечно).


Страницы:   1 2 3

Наверх

Титульная страница Поиск, карта сайта Написать письмо