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

Franzy ©   (2008-11-17 14:14:00) 

Пишу прогу, в которой приходится работать с большими массивами данных. Прога загружает файл 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 вместо повторного использования зарезервированных ранее сегментов памяти резервирует новые...

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


  (2009-04-01 12:05:00) [1]

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


Amoeba   (2008-11-20 15:44:00) [2]


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

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


Anatoly Podgoretsky   (2008-11-19 23:53:00) [3]

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

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


sniknik   (2008-11-19 23:04:00) [4]

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

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


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


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

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


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


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


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


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

аналогично, но я вижу дурное в подобном
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
...хотя читал, честно говоря, "по диагонали", в каких-то деталях могу ошибаться.


Anatoly Podgoretsky   (2008-11-19 19:33:00) [8]


> SetProcessWorkingSetSize не поможет

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


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

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


sniknik   (2008-11-19 19:21:00) [9]

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

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

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

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

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


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

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

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

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


sniknik   (2008-11-19 11:26:00) [11]

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

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


Franzy   (2008-11-19 11:08:00) [12]

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


sniknik   (2008-11-17 16:54:00) [13]

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

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

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


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

2Sapersky

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


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

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


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

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

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


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

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

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


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

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

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


Anatoly Podgoretsky   (2008-11-17 15:17:00) [19]

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

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


Anatoly Podgoretsky   (2008-11-17 15:15:00) [20]

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

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


Страницы:   1 2

Наверх

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