Форум: "Основная";
Текущий архив: 2015.10.11;
Скачать: [xml.tar.bz2];
ВнизOut of memory после полумесяца работы программы. Найти похожие ветки
← →
Дмитрий Белькевич (2011-06-07 10:46) [0]Утечек памяти нет (судя по эврикалог). Out of memory случается где-то на 10-15 день работы приложения. Случается при попытке выделения памяти под динамический массив:
SetLength(FBuff16, AllocSLiceSz div 2);
AllocSLiceSz равен 31768000, т.е. выделяется около 31 мб.
Использую менеджер памяти FastMM.
Вопросов, как обычно, два - кто виноват и что делать...
← →
Anatoly Podgoretsky © (2011-06-07 11:02) [1]Странный вопрос "кто виноват", ответ бить. На лицо фрагментация памяти.
← →
clickmaker © (2011-06-07 11:14) [2]> что делать
как вариант - перезапускать раз в несколько суток (планировщиком задач, к примеру)
Либо пересмотреть логику распределения памяти
← →
Ega23 © (2011-06-07 11:50) [3]
> Случается при попытке выделения памяти под динамический
> массив:
Фрагментация.
Как вариант - либо перезапускать время от времени, либо сразу здоровый массив выделить на 100 мб и работать с ним постоянно, без реаллоков.
← →
QAZ (2011-06-07 13:02) [4]а зачем эврика если FastMM сам все тожет определять
← →
RWolf © (2011-06-07 13:14) [5]это какой-то временный буфер? может, размещать его на стеке?
← →
Sha © (2011-06-07 13:50) [6]Можно его не удалять, чтобы потом не создавать.
← →
RWolf © (2011-06-07 13:55) [7]или создавать, но не из-под FastMM, а средствами ОС.
← →
Дмитрий Белькевич (2011-06-07 14:24) [8]Приложение - довольно массивный сервер, делающий много всякого разного - раздающий веб, работающий с десятками потоков. И там далеко не один этот массив используется - что бы вот так - сделать статическим - и решить проблему. Всякого разного динамического там много.
Я тоже думаю на фрагментацию памяти. Потому как валится именно после того, как долго поработают. Пробовали несколько раз перезапускать после трех-четырех дней работы - тогда работает нормально, до какого-то времени - дней 12-15, как писал.
Но всё таки удивительно - неужели реаллоки памяти могут так сильно её фрагментировать, что 32-х мегабайтный сплошной фрагмент найти невозможно?
Еще вопрос - можно ли фастмму как-то сказать, что бы он дефрагментировал память? Можно ли что-то кроме перезапуска придумать? Предполагается, что сервер доступен постоянно (насколько железо позволяет, конечно).
← →
RWolf © (2011-06-07 14:40) [9]Если буферы выделять средствами операционки (т.е. HeapAlloc), можно время от времени делать HeapCompact; по идее, это должно убирать фрагментацию памяти процесса.
← →
Sha © (2011-06-07 16:06) [10]> Дмитрий Белькевич (07.06.11 14:24) [8]
> И там далеко не один этот массив используется - что бы вот так -
> сделать статическим - и решить проблему.
> Всякого разного динамического там много.
Можно организовать повторное использование только больших кусков.
← →
Дмитрий Белькевич (2011-06-07 16:10) [11]Попробовал тестово повыделять память:
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 же дней ждать :)
← →
sniknik © (2011-06-07 16:19) [12]может зря на память "гонишь", может что-то в логике? (вряд ли эврикалог/... отследит), ну к примеру потоки не завершаются (не падают/выходят без очистки/..., а именно просто впустую крутятся, аля зомби поток...)
← →
_Юрий (2011-06-07 20:25) [13]У меня ровно тоже самое:
COM - сервер, утечек нет, судя по показаниям FastMM
А вылет с OutOfMemory где-то раз-два в месяц.
← →
_Юрий (2011-06-07 20:28) [14]Но у меня непрерывные большие куски не выделяются. Никаких массивов, только списки объектов
← →
Sha © (2011-06-07 20:29) [15]Вот такая процедура вылетает у меня (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-08 00:02) [16]
> может зря на память "гонишь", может что-то в логике? (вряд
> ли эврикалог/... отследит), ну к примеру потоки не завершаются
> (не падают/выходят без очистки/..., а именно просто впустую
> крутятся, аля зомби поток...)
А я вижу картину падения в эврикалоге. Увы - ничего крамольного.
Полный кусок кода:
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;
Так вот - эврика успешно создаёт лог, ничего особенного в нём нет.
← →
Дмитрий Белькевич (2011-06-08 00:10) [17]Эврика список всех потоков приложения показывает, если что. Всё в норме - штук 15 крутится всякого-разного, которое и так почти всегда работает.
> Вот такая процедура вылетает у меня (D7 без FastMM) на 1543
> проходе цикла,
Подтверждаю. Реально ли это исправить, кроме как рестартом?
← →
Sha © (2011-06-08 01:18) [18]> Реально ли это исправить, кроме как рестартом?
Чтобы было совсем нереально, наверно, постараться надо.
Конкретный рецепт, конечно, от программы зависит, но основные идеи:
для больших кусков запрашивать памяти больше, чем надо, и повторно использовать,
для маленьких - запрашивать память в правильной последовательности, не допускать фрагментации,
возможно, удобнее будет от строк перейти к буферам.
← →
Sha © (2011-06-08 01:24) [19]В [18] имеется в виду переход к пулу буферов фиксированного размера,
которые освобождаются только по окончании работы программы.
← →
sniknik © (2011-06-08 07:58) [20]> Реально ли это исправить, кроме как рестартом?
всегда обнулял в финале, если выделял, после использования, проблем не было (по аналогии раз выделил освободи)
тут, типа -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;
← →
Дмитрий Белькевич (2011-06-08 08:38) [21]
> Конкретный рецепт, конечно, от программы зависит, но основные
> идеи:для больших кусков запрашивать памяти больше, чем надо,
> и повторно использовать,для маленьких - запрашивать память
> в правильной последовательности, не допускать фрагментации,
> возможно, удобнее будет от строк перейти к буферам.
Всего этого придерживаюсь. Когда массивам записей SetLength делаю - выделяю по сто или по 1000 элементов за раз, позже обрезаю до нужного размера.
Но вот - всё равно вылазит Out of memory.
Интересна именно дефрагментация памяти. В FastMM готового или не нашел или плохо искал. Не представляю даже, что делать, кроме рестарта. Рыть менеджер памяти самому - как-то не хочется...
← →
Sha © (2011-06-08 08:58) [22]> 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 проходе цикла.
← →
Sha © (2011-06-08 08:59) [23]... откуда берется фрагментация.
← →
Anatoly Podgoretsky © (2011-06-08 09:00) [24]
> Всего этого придерживаюсь. Когда массивам записей SetLength
> делаю - выделяю по сто или по 1000 элементов за раз, позже
> обрезаю до нужного размера.
>
> Но вот - всё равно вылазит Out of memory.
Соотвественно ты уже не сможешь выделить тот же блок на 1000 элементов, а только новый, вот так и получает фрагментация и следствие
← →
sniknik © (2011-06-08 09:42) [25]> Простого рецепта, типа "освободи, когда станет ненужным" не существует.
а у меня это не рецепт, это правило (еще с паскаля). которое судя по всему сильно снижает вероятность подобных эксцессов.
> позже обрезаю до нужного размера.
зачем? тут ИМХО тоже кроется возможность фрагментации, ну типа выделили 1000 после еще байт 10 в другом массиве, у после обрезали первый до 20 байт... куда денутся оставшиеся 980?
и пусть даже следом/в другом цикле после наслоение следующих выделений будет очистка массивов 10 и 20 то будет ли перераспределение на "потерянные" 980???
← →
KSergey © (2011-06-08 13:43) [26]У меня есть дурацкий (не подходящий для этого случая, увы) рецепт: переходить на .NET - тама дефрагментация .NET-ядром делается периодически, волшебно или на ручной тяге.
Ну или подобным образом переписывать свою прогу.
Вот только это нереально, увы.
PS
У нас сервер всего на работу в течение дня рассчитан, и то частенько не доживает :( Бороться трудно
← →
Sha © (2011-06-08 14:20) [27]> выделяю по сто или по 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;
← →
Loginov Dmitry © (2011-06-08 16:18) [28]
> Вот такая процедура вылетает у меня (D7 без FastMM) на 1543
> проходе цикла,
D2010. 2ГБ ОЗУ. Ошибка возникает на 734085 итерации. При этом под строку "s" выделено памяти 735085000 байт. Что волшебного в менеджере памяти у D2010?
← →
sniknik © (2011-06-08 17:27) [29]> Что волшебного в менеджере памяти у D2010?
а вроде что-то про "сборщик мусора" с какой то версии дельфи говорили... ?
вот оно волшебство то.
← →
RWolf © (2011-06-08 17:50) [30]у FastMM или менеджера D7 есть вызовы для определения максимального размера непрерывной области памяти, которая может быть выделена?
неплохо было бы мониторить это число при отладке, чтобы избежать сабжа.
← →
Sha © (2011-06-08 18:05) [31]> Loginov Dmitry © (08.06.11 16:18) [28]
> Что волшебного в менеджере памяти у D2010?
Не перемежает разнокалиберные запросы.
← →
Loginov Dmitry © (2011-06-08 21:12) [32]
> При этом под строку "s" выделено памяти 735085000 байт.
Кстати обманул, указал количество символов в строке, а не размер выделенной под нее памяти. В случае D2010 число нужно умножать на 2, поэтому получим ~ 1,5 ГБ.
← →
han_malign (2011-06-09 10:16) [33]
> Простого рецепта, типа "освободи, когда станет ненужным" не существует.
- простой рецепт - как можно меньше вложенных динамических объектов...
В большинстве случаев, вложенные String прекрасно заменяются на String[адекватного размера], а вложенные классы на статически размещенные структуры/объекты...
← →
Дмитрий Белькевич (2011-06-09 15:43) [34]Позже вернусь к вопросу, попробую полную отладку в fastmm включить + поиск границ и иже. Возможно всё таки это не фрагментация, хотя я уверен процентов на 80.
← →
dred2k (2011-06-15 20:55) [35]Советую настроить мониторинг приложения в мониторе ресурсов (системном). Промониторить все пулы памяти - выгружаемый, невыгружаемый и т.д. С записью в файл, потом - анализ. Было дело, с помощью этого выловил регулярные падения сервака по вине, как оказалось, стороннего драйвера (юзал ком-порты через шарилку - SAPS).
Тут может быть _все что угодно_. При современном уровне порой очень не очевидной интеграции. Кстати, а DLL используются в алгоритме ?
← →
dred2k (2011-06-15 20:56) [36]В чем у меня была сложность - падала NT-я. :)
← →
dred2k (2011-06-15 21:15) [37]На Delphi2007 тоже "волшебный" менеджер (я на тему [28]).
864272 (EOutOfMemory) = [865271000 - длина строки s]
Win7x64, 4 гига
← →
dred2k (2011-06-15 21:34) [38]Кстати, рекомендую тулзу Process Explorer из SysInternals, он показал в свойствах процесса Peek Private Bytes как раз равным размеру строки s - 800 с чем-то метров. При этом Virtual Size был около 65 метров.
Может подумать об использовании функций класса Virtualxxx ?
Быстрые гипотезы, сорри.
← →
dred2k (2011-06-15 23:36) [39]Решил поэкспериментировать со строками. Как при операциях меняется указатель на первый элемент ?
Ниже - пример, для внутренней переменной. Если перенести ее в глобальную область - будет интереснее...procedure TForm1.Button2Click(Sender: TObject);
var tmpStr : String;
begin
// Посмотрим, как изменяются указатели на первый символ
// одной и той же строки при некоторых операциях с последней.
with Memo1.Lines do
begin
Clear;
// Вариант 1
Add("Вариант 1");
tmpStr := "1234";
Add(IntToStr(Integer(@tmpStr[1])) + " ["" + tmpStr + ""]");
tmpStr := tmpStr + "1234567";
Add(IntToStr(Integer(@tmpStr[1])) + " ["" + tmpStr + ""]");
tmpStr := "1234567890";
Add(IntToStr(Integer(@tmpStr[1])) + " ["" + tmpStr + ""]");
Add("------------------------------------");
// Вариант 2
Add("Вариант 2");
tmpStr := "1234";
Add(IntToStr(Integer(@tmpStr[1])) + " ["" + tmpStr + ""]");
tmpStr := tmpStr + "12345678"; // <---- !!!!!!!!!!!!!!!!!!!!!
Add(IntToStr(Integer(@tmpStr[1])) + " ["" + tmpStr + ""]");
tmpStr := "1234567890";
Add(IntToStr(Integer(@tmpStr[1])) + " ["" + tmpStr + ""]");
Add("------------------------------------");
// Вариант 3
Add("Вариант 3");
tmpStr := "1234";
Add(IntToStr(Integer(@tmpStr[1])) + " ["" + tmpStr + ""]");
tmpStr := tmpStr + "1234567";
Add(IntToStr(Integer(@tmpStr[1])) + " ["" + tmpStr + ""]");
tmpStr := "12345678901"; // <---- !!!!!!!!!!!!!!!!!!!!!
Add(IntToStr(Integer(@tmpStr[1])) + " ["" + tmpStr + ""]");
Add("------------------------------------");
// Вариант 4
Add("Вариант 4");
tmpStr := "1234";
Add(IntToStr(Integer(@tmpStr[1])) + " ["" + tmpStr + ""]");
tmpStr := tmpStr + "1234567";
Add(IntToStr(Integer(@tmpStr[1])) + " ["" + tmpStr + ""]");
tmpStr := "123456789012"; // <---- !!!!!!!!!!!!!!!!!!!!
Add(IntToStr(Integer(@tmpStr[1])) + " ["" + tmpStr + ""]");
Add("------------------------------------");
end;
end;
← →
Дмитрий Белькевич (2011-06-15 23:57) [40]
> Тут может быть _все что угодно_.
Вообще, замечено, что, вероятно, проблема в обработке веба. Сервер, среди прочего, имеет веб интерфейс к себе и генерирует (без сторонних приложений) некоторое количество - штук 30-40 разных страниц. Так вот - у нас на основном "тестовом полигоне" сервер нормально работает месяцами, но мы у себя веб не используем. У одних из пользователей, по соглашению - бэта-тестеров - сервер используется полностью, с вебом в том числе. Это, правда, слабое утешение в поиске проблемы. Опять же - возможно, что именно веб дает сильную фрагментацию памяти. Мы пока что переползли на обновленный фаст мм, попробуем еще с ним поработать. Дальше буду пробовать фуллдебаг мод. Буду пробовать снимать карту памяти (вроде бы фаст мм позволяет) после out of memory (если удастся). Попробую еще какую-то информацию вытянуть из фастмм, если не удастся встроенными средствами - попробую фастмм дописать.
Страницы: 1 2 вся ветка
Форум: "Основная";
Текущий архив: 2015.10.11;
Скачать: [xml.tar.bz2];
Память: 0.57 MB
Время: 0.012 c