Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2010.02.28;
Скачать: [xml.tar.bz2];

Вниз

Out of Memory - непонятки   Найти похожие ветки 

 
Franzy   (2008-11-17 14:14) [0]

Пишу прогу, в которой приходится работать с большими массивами данных. Прога загружает файл STL (примитивный формат СAD, фактически - список треугольников в формате: список кооржинат узлов плюс нормаль), совершает некие операции последовательно с каждым треугольником, затем записывает результат операций в другой файл.

С тестовыми (небольшими) файлами работает. С большими - в определенный момент вылетает с ошибкой Out Of Memory. Проблема в том, что, по моим расчетам, памяти должно хватать, причем с лихвой. При счете открываю Диспетчер задач, вижу по графику, что жрет 100 мегов там, где должно хватать одного мега. Т.е. имеет место memory leak, который я не могу определить.

Объектов никаких не использую, зато использую динамические массивы (а также динамические массивы динамических массивов). Возможно, я как-то не так освобождаю память?

Вот характерный пример используемых мною структур данных:

 Type

 TBaseContour = record
   length : longint; //кол-во узлов
   node : array of longint; //список узлов базового контура
 End;

 TBaseFace = record
   contour : array of TBaseContour; //список баз. контуров грани
   nContours : longint; //Кол-во контуров в грани
   facet : array of longint; //список номеров треугольников, образующих грань
   nfacets: longint; //кол-во тр-ков
 End;

 TBaseVolume = record
   face : array of TBaseFace; //грани, образующие объект
   nFaces : longint; //кол-во граней
 end;

 Var

 volume : array of TBaseVolume;
 nVolumes : longint; // number of volumes


Заранее длины дин. массивов не известны, поэтому в процессе счета их длины меняются при необходимости с помощью SetLength.

Это глобальные структуры, которые заявляются один раз (в них записываются результаты). Плюс я при анализе каждого треугольника использую дополнительные "одноразовые" массивы (тоже динамические). Чтобы освободить память после использования такого массива я пишу: [имя массива]:=nil;  

Но складывается ощущение, что память при этом не освобождается, т.е. Windows вместо повторного использования зарезервированных ранее сегментов памяти резервирует новые...

Может, я неправильно освобождаю память? Или проблема в структурах, включающих в себя динамические массивы?


 
tesseract ©   (2008-11-17 14:29) [1]


> Но складывается ощущение, что память при этом не освобождается,


SetLength(mass,0)? Кучу может и не отпускать, см профайлером.


 
Franzy   (2008-11-17 14:34) [2]

Еще один момент: "одноразовые" динамические массивы у меня заявляются как локальные переменные (внутри процедуры-обработчика). Это что-то меняет?


 
MBo ©   (2008-11-17 14:37) [3]

>Заранее длины дин. массивов не известны, поэтому в процессе счета их длины меняются при необходимости с помощью SetLength.

Если длина массивов каждый раз увеличивается на единицу, то будет большой перерасход памяти. Выход - держать счетчик использованных элементов и увеличивать длину при заолнении массива на значительное число, или увеличивать вдвое. Можно и сразу задать длину с запасом.


 
Franzy   (2008-11-17 14:43) [4]


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


Почему?


 
Anatoly Podgoretsky ©   (2008-11-17 14:56) [5]

> Franzy  (17.11.2008 14:34:02)  [2]

Ничего не меняет, массив занимает всего 4 байта, а данные хранятся в куче.


 
sniknik ©   (2008-11-17 14:57) [6]

потому, что увеличение делается не "растягиванием" используемой под массив памяти на размер ещё одного элемента, а выделением нового блока большего на этот размер и копированием в него содержимого первого блока.
память же из под первого "теряется", т.к. выделит её повторно при запрашивании следующего блока проблематично... размер этого блока будет гарантированно меньше т.к. следующий будет ещё на единицу больше...


 
han_malign ©   (2008-11-17 14:58) [7]

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


 
Anatoly Podgoretsky ©   (2008-11-17 14:59) [8]

> Franzy  (17.11.2008 14:43:04)  [4]

Потому что, для перераспределения массива, старый блок использовать не получится, он меньше чем нужно и поэтому будет выделяться новый блок и содержимое будет копироваться в него и будет постоянный рост памяти, на каждом шаге 2N+1
При больших размерах память быстро кончится. Нужна реализация наращивания памяти не по единицы, а например сразу на 25% и самостоятельно отслеживать выход за границу. Алгоритм реализован в TList


 
Sapersky   (2008-11-17 15:01) [9]

Franzy   (17.11.08 14:43) [4]

Ну есть такой недостаток у дельфийского менеджера памяти для версий < 2006. То ли чрезмерная фрагментация блоков у него начинается, то ли ещё что-то. Видимо, он больше рассчитан на использование в стиле TList, с выделением большого кол-ва маленьких кусочков без изменения размера.

В 2006 и далее встроен FastMM, у которого, вроде, такой проблемы нет. FastMM, кстати, можно использовать и с более ранними версиями.

Другой вариант - как уже говорили, перевыделять большими блоками. Пример:
http://sapersky.narod.ru/files/Arrays.rar


 
Franzy   (2008-11-17 15:04) [10]

Все ясно. Да, дело скорее всего в этом. Честно говоря, надеялся, что в Дельфях работа с памятью получше организована... :(


 
Franzy   (2008-11-17 15:09) [11]

А если длина массива уменьшается, он как, остается на месте или тоже перезаписывается целиком в новое место?


 
Anatoly Podgoretsky ©   (2008-11-17 15:13) [12]

> sniknik  (17.11.2008 14:57:06)  [6]

Растягивание тоже делается, если блок крайний.


 
Anatoly Podgoretsky ©   (2008-11-17 15:15) [13]

> Franzy  (17.11.2008 15:04:10)  [10]

Диспетчер памяти всегда компромис, между скорость и ресурсами.


 
Anatoly Podgoretsky ©   (2008-11-17 15:17) [14]

> Franzy  (17.11.2008 15:09:11)  [11]

Сделай тесты, наращивай массив в цикле выводи значение адреса, тоже сделай и в реальной программе.


 
Sapersky   (2008-11-17 15:23) [15]

А если длина массива уменьшается, он как, остается на месте или тоже перезаписывается целиком в новое место?

По идее должен оставаться, хотя зависит от конкретной реализации менеджера памяти.
Однозначно перезаписывается - если RefCount > 1, т.е. на данный блок ссылаются две и более переменных-массива (было присвоение arr2 := arr1).


 
sniknik ©   (2008-11-17 15:23) [16]

> А если длина массива уменьшается, он как, остается на месте или тоже перезаписывается целиком в новое место?
а сам то ты как думаешь? представь что ты разрабатываешь свой менеджер памяти
вот тебе 4 массива в памяти, как ты увеличишь первый на единицу? желательно затратив меньше ресурсов/времени, так чтобы не было "такой проблемы"?
111222233333444444
есть вообще возможность оставить его на месте? и второй вопрос, есть ли возможность оставить его на месте при уменьшении?
имхо, это очевидно. просто чуть логики.

> Растягивание тоже делается, если блок крайний.
логично, но имхо, это бывает крайне редко...


 
Anatoly Podgoretsky ©   (2008-11-17 15:37) [17]

> sniknik  (17.11.2008 15:23:16)  [16]

Конечно редко, поскольку трудно обеспечить, что ни будь уже захватило следующий байт вот и придется копировать.
Я кстати предложил сделать простой тест, в цикле ситуация расширения весьма вероятна. Во всяком случае покажет как это работает.


 
Franzy   (2008-11-17 15:40) [18]

По идее, конечно, массив должен на месте остаться. Но черт знает этот менеджер... Потому и спрашиваю :)


 
Franzy   (2008-11-17 16:03) [19]

2Sapersky

Скачал FastMM4, скомпилировал с ним... Библиотека творит чудеса! Больше память никуда не теряется. Спасибо!!!


 
sniknik ©   (2008-11-17 16:54) [20]

> Скачал FastMM4, скомпилировал с ним... Библиотека творит чудеса! Больше память никуда не теряется. Спасибо!!!
она и без неё никуда не теряется, в прямом смысле, в переносном же она потеряна для работы т.к. ты её зафрагментировал неправильним использованием по самое не могу...
т.е. чудо только в том, видимо, что она временами проводит "сборку мусора" объединяет неиспользуемые куски в большие блоки. (фактически "замазывает" твои ошибки, ведь неправильная работа никуда не делась... просто после кто то исправляет последствия)
насчёт сборки мусора в FastMM4, это конечно имхо, догадки, на самом деле не знаю как она реально работает.

но можно проверить. вот эта функция сделает примерно тоже самое (подожмёт используемую память, уберет фрагментированные блоки, и отдаст "лишнее" системе)
SetProcessWorkingSetSize(GetCurrentProcess, dword(-1), dword(-1));
убери FastMM4, и сам время от времени вызывай эту функцию. результат должен быть аналогичным (если я прав конечно).

это я к чему, библиотеки это дело хорошее (хорошие библиотеки)... но и самому надо писать правильно. (типа, на Гейтца надейся а сам не плошай. :))


 
Franzy   (2008-11-19 11:08) [21]

Размер pas-файла FastMM - 340кб. Вряд ли там только это :) Она полностью заменяет менеджер памяти Дельфей.


 
sniknik ©   (2008-11-19 11:26) [22]

никто и не говорил что "там только это", но это именно та часть которая прячет твои ошибки по работе с памятью.

трудно что ли проверить? рем на обьявление модуля FastMM4 поставить и в цикл себе вставить указанную функцию?
слишком частый вызов скорее всего приведет к тормозам, но проблему нехватки памяти уберет, и если уберет... ну. это повод задуматься. чего ты до сих пор кажется избегаешь.  нашёл себе "волшебный" модуль, списал свои глюки на неправильную работу менеджера дельфи и тихо радуешься, думать не надо...


 
Sapersky   (2008-11-19 15:50) [23]

Фрагментация, ИМХО, возникает на уровне дельфийского менеджера памяти, а не системного, так что SetProcessWorkingSetSize не поможет. И в FastMM эта функция не используется.

Ничего дурного в решении проблемы с использованием FastMM не вижу. Почему бы не переложить рутину на "волшебный" модуль? По сути, насколько я смог разлядеть, он автоматически делает то, что пришлось бы делать вручную - выделяет память "с запасом". Да, вручную, возможно, получилось бы эффективнее - но в большинстве случаев "овчинка выделки не стоит".

Опять же, по вашей логике следует отказаться и от стандартного дельфийского менеджера... ведь он тоже "замазывает" кое-какие ошибки - выделение большого кол-ва маленьких блоков, например.
Позор! Нормальный программист должен всё делать вручную! :)


 
sniknik ©   (2008-11-19 19:21) [24]

> Фрагментация, ИМХО, возникает на уровне дельфийского менеджера памяти, а не системного, так что SetProcessWorkingSetSize не поможет.
истинно так, именно в дельфийском, но функция требует от программы освободить неиспользуемую память, что без дефрагментации (поджимания используемых блоков начало, и отделения неиспользуемого "конца") невозможно.
спорить не буду, с самого начала сказал что не уверен, и надо проверить, без этого все переливание из пустого в порожнее.

> И в FastMM эта функция не используется.
а кто такое сказал? плюнь ему в глаза, или в зеркало. и вообше читай написаное, не выдумывай.
было сказано "эта функция сделает примерно тоже самое", а не "там используется вот это ..."
 

> Ничего дурного в решении проблемы с использованием FastMM не вижу.
аналогично, но я вижу дурное в подобном
for i:= 1 to 1000000 do begin
 SetLength(mas, i);
 ... код использующий добавку к массиву на еденицу...
end;

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

> ведь он тоже "замазывает" кое-какие ошибки - выделение большого кол-ва маленьких блоков, например.
бред. даже не смешно.


 
Anatoly Podgoretsky ©   (2008-11-19 19:33) [25]


> SetProcessWorkingSetSize не поможет

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


> Позор! Нормальный программист должен всё делать вручную!
>  :)

Должен уметь делать вручную!, а дальше в силу вступает правило - должен, но не обязан.


 
Sapersky   (2008-11-19 21:07) [26]

аналогично, но я вижу дурное в подобном
for i:= 1 to 1000000 do begin
SetLength(mas, i);
... код использующий добавку к массиву на еденицу...
end;


А если там будет TList.Add? Конечно, лучше заранее выставить Capacity - но допустим, что оно неизвестно.
А если алгоритм перевыделения наподобие TList реализован непосредственно в менеджере? Судя по комментариям в FastMM - он именно так и работает:

     {This pointer is being reallocated to a larger block and therefore it is
      logical to assume that it may be enlarged again. Since reallocations are
      expensive, there is a minimum upsize percentage to avoid unnecessary
      future move operations.}
     {Must grow with at least 100% + x bytes}
     LNewAllocSize := LOldAvailableSize * 2 + SmallBlockUpsizeAdder;

Для "средних" блоков перевыделение делается экономнее:

       {Couldn"t upsize in place. Grab a new block and move the data across:
        If we have to reallocate and move medium blocks, we grow by at
        least 25%}
       LMinimumUpsize := LOldAvailableSize + (LOldAvailableSize shr 2);

Для "больших" тоже 25%.

> ведь он тоже "замазывает" кое-какие ошибки - выделение большого кол-ва маленьких блоков, например.
бред. даже не смешно.


Ну то есть выделение большого кол-ва маленьких блоков через дельфийский менеджер будет быстрее, чем через системный, т.к. дельфийский запрашивает у системы сразу много памяти (минимизируя кол-во системных вызовов) и затем отдаёт программе по кусочкам.
Во всяком случае, так я понял статью:
http://www.rsdn.ru/article/Delphi/memmanager.xml
...хотя читал, честно говоря, "по диагонали", в каких-то деталях могу ошибаться.


 
novex   (2008-11-19 21:30) [27]


> Чтобы освободить память после использования такого массива
> я пишу: [имя массива]:=nil;


я чето невкурю, а разве больше не надо делать free? а потом := nil?
меня в школе учили что выделенную память всегда нужно освобождать самому и не надеятся на сборшики мусора, неужели что-то изменилось?


 
Германн ©   (2008-11-19 21:56) [28]


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

Да. Появились динамические массивы. :)


 
sniknik ©   (2008-11-19 23:04) [29]

> Судя по комментариям в FastMM - он именно так и работает:
> ...
ну, вот этого я как раз и не знал, думал он идет "от обратного" и наоборот собирает в кучу всю бездумно зафрагментированную программистом память. но я честно предупреждал что не знаю как он на самом деле работает...

> Во всяком случае, так я понял статью:
все правильно, но почему ты это назвал ошибками и "выделением большого кол-ва маленьких блоков", реально то наоборот, запрашивается один большой, который и распределяется после.


 
Anatoly Podgoretsky ©   (2008-11-19 23:53) [30]

> novex  (19.11.2008 21:30:27)  [27]

Какое еще Free - это же не объект


 
Amoeba ©   (2008-11-20 15:44) [31]


> novex   (19.11.08 21:30) [27]
>
>
> > Чтобы освободить память после использования такого массива
> > я пишу: [имя массива]:=nil

В случае динамических массивов (и длинных строк) компилятор самостоятельно добавляет код выполняющий освобождение выделенной памяти.
http://www.delphikingdom.com/asp/viewitem.asp?catalogid=1184


 
имя   (2009-04-01 12:05) [32]

Удалено модератором



Страницы: 1 вся ветка

Форум: "Основная";
Текущий архив: 2010.02.28;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.55 MB
Время: 0.005 c
4-1229417136
Riply
2008-12-16 11:45
2010.02.28
Настоящее закрытие файла :)


4-1229520484
belmol
2008-12-17 16:28
2010.02.28
работа с dll


15-1260451652
Владислав
2009-12-10 16:27
2010.02.28
Зависание системы при отладке.


1-1237789977
Riply
2009-03-23 09:32
2010.02.28
Получение EnvironmentStrings среды.


2-1260884519
Агафон
2009-12-15 16:41
2010.02.28
Как вставить картинку????????





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский