![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Out of memory после полумесяца работы программы.
Дмитрий Белькевич © (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-х мегабайтный сплошной фрагмент найти невозможно?
Еще вопрос - можно ли фастмму как-то сказать, что бы он дефрагментировал память? Можно ли что-то кроме перезапуска придумать? Предполагается, что сервер доступен постоянно (насколько железо позволяет, конечно).