Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 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
2-1400610990
Den
2014-05-20 22:36
2015.10.11
Вставить ixmlDomNode перед определенной ixmlDomNode?


15-1424182937
Torry
2015-02-17 17:22
2015.10.11
Продажа torry.net


2-1400838886
mfender
2014-05-23 13:54
2015.10.11
Ерунда с записью/чтением структуры в потоке


1-1333555620
dreamse
2012-04-04 20:07
2015.10.11
Загрузка DLL из другой DLL


2-1400762205
IceBeerg
2014-05-22 16:36
2015.10.11
Как изменить Объект ярлыка





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский