Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Резкое увеличение размера динамического массива [D6, WinXP]


Страницы:   1 2 3

vint45 ©   (2007-10-11 16:25:00) 

Здраствуйте!

Есть динамический массив, в который помещаются записи (record) с описанием файлов при сканировании диска. Записи такого вида:
 TFile = record
  Folder: string;
  FullName: string;
  Size: int64;
 end;


При сканировании всего диска создается довольно большой массив, около 60000 элементов. При этом программа вылетает с ошибкой "Out of memory".
Диспетчер задач показывает, что программа съела около 250 метров. Хотя если подсчитать сколько должен занимать массив (подсчетом длины строк помещаемых в массив + указатели), то получится примерно метров 5.
Замена типа string (AnsiString) на PChar для FullName решает проблемму (диспетчер показывает, что приложение съедает мах 3 метра). Но вопрос остается, почему при работе с AnsiString приложение выделяет так непропорционально память.
Может так диспетчер памяти выделяет блоки с резервироваем пространства, или может я все-таки что-то не так делаю (тогда могу привести весь код)?


Sapersky   (2007-10-12 16:11:00) [1]

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

Он скорее дополняющий, чем альтернативный. Реализует алгоритм подобный тому, что используется в TList, для обычных дин. массивов (Length(array) играет роль Capacity, реальная длина хранится в отдельной переменной).
Используется примерно так:

Type
PFile = ^TFile;
TFileArr = array of TFile;

...

  arrFiles: array of TFile;
  FileCnt : Integer = 0;

...

With PFile( Arr_AddItem(arrFiles, TypeInfo(TFileArr), FileCnt) )^ do begin
  Folder:=InitPath;
  FullName:=Path+sr.Name;
  Size:=sr.Size;
end;

Метод перевыделения памяти "в 2 раза больше, чем было" (см. Arr_Grow) может показаться чрезмерно экстремальным, но на практике он показывает себя лучше, чем "экономное" приращение на 1 элемент (в вашем примере - 5 мб по диспетчеру задач). Хотя не буду утверждать, что это идеальное решение для всех случаев.


vint45   (2007-10-12 15:47:00) [2]

Насколько я понял из постов
> Galinka ©   11.10.07 22:26) [24]и
> Anatoly Podgoretsky ©   (11.10.07 22:28) [25]
лавинообразное увеличение памяти связано с особенностью работы менеджера памяти D6. Бороться с этим можно:
1. > vpbar ©   (11.10.07 20:08) [22]
Заменой динамических массивов на использование TList (минус этого, имхо работать с динамическими массивами легче, но это чисто субъективно)
2. > Sapersky   (11.10.07 19:59) [19]
Использованием альтернативного модуля для работы с массивами (ничего сказать не могу, т.к. не работал с модулем)
3. Есть третий вариант. Замена AnsiString на PChar, т.е. в массив будут помещаться строки PChar. Менеджер памяти в этом случае ведет себя корректно, по крайней мере менеджер задач показывает выделение памяти для процесса в пределах 3Мб. Правда не факт, что в других случаях, менеджер будет вести себя столь же корректно, потому как мне не понятны отличия при выделении памяти AnsiString и PChar (хотя PChar "родной" тип винды и для работы с ним вроде как используются системные функции, а следовательно системный менеджер памяти).

зы: в подтвержние об освождении фрагментированной памяти я провел такой эксперимент со строками AnsiString. Задал для сканирования довольно большой каталог с условием, чтобы выделялось достаточно большой объем памяти, но программа при этом не вылетала с ошибкой "Out of memory". После сканирования диспетчер задач показывал объем выделенной памяти около 100Мб, а после наступления определенного события (у меня например после минимизации окна приложения), диспетчер показал 1Мб :)


Германн   (2007-10-12 00:56:00) [3]

Блин. Даже я ошибся. :-(

Вот цитата из первоисточника:
Горбушка, презиравший рассуждения о высоких материях, был больше поэтом и назвал свой журнал исключительно поэтично:
ЗОРИ
     Однако Горбушка при всех своих поэтических талантах был безграмотен и уже с первого номера скандально опростоволосился.
     На первой странице Горбушкиного издания по случаю бывшего месяца три назад спектакля красовался рисунок из пушкинского "Бориса Годунова".
     Рисунок Горбушки изображал Японца в роли Годунова, с большим жезлом в руке.
     Но не рисунок заставил всю школу покатываться со смеху, а пояснительная надпись под ним: Юлыстрация к трогедие "Борис Гадунв".
     Горбушка умудрился в пяти словах сделать семь ошибок и здорово поплатился.
     Поэтичные "Зори" читали все и не потому, что шкидцев очень уж интересовала поэзия, их читали как .хороший юмористический журнал, и даже Янкель обижался:
     - Сволочь этот Горбушка... Конкурент.


Германн   (2007-10-12 00:45:00) [4]


> про соверменный манарег

Про что, про что ты говоришь?
:-)

P.S. Кроме меня это помнит только Кэтмар:
"Юлыстрация к трогедии Борис Гадунв"


Суслик   (2007-10-12 00:35:00) [5]

Алгоритма из TList достаточно для любых практических задач.
Говорю как про Д7, так и про соверменный манарег памяти в Д2007 и Д2006.


Суслик   (2007-10-12 00:33:00) [6]

в современных версяих дельфи СОВСЕМ-СОВСЕМ другой манагер памяти.
Просто ничего общего.

Тот, что описан на Королевсте (мистик, кстати описывал) сложнее теперешнего раз в 100. Теперь FastMM. Он и быстрее и проще.


Anatoly Podgoretsky   (2007-10-11 23:27:00) [7]

> Galinka  (11.10.2007 23:22:35)  [35]

Если задача повторяемая, то задача упрощается, запоминаем старое значение, а при новом прогоне выделяем на N% больше. Это к обсуждаемой проблеме. Иначе или эмпирически или по алгоритму TList


Galinka   (2007-10-11 23:22:00) [8]

Anatoly Podgoretsky ©   (11.10.07 23:12) [33]

ну я так и сделала. Но предварительно надо откуда-то узнать, сколько папок/папок в каталоге. Если это можно как-то оценить, то и задача значительно упрощается. Короче лучше потерять "пару минут" на определение общего кол-ва файлов и длинны "пути", а потом один раз выделить память.


Galinka   (2007-10-11 23:16:00) [9]

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

Т.е. не экономя "пару байтов" на "длине строки", он выигрывал "точное знание" адресов и размеров для объединения уже освобожденных блоков. И не допускал фрагментации.


Anatoly Podgoretsky   (2007-10-11 23:12:00) [10]

> Anatoly Podgoretsky  (11.10.2007 23:10:32)  [32]

Но можно сменить алгоритм на другой

array[60000]
S1
...
Sn


Anatoly Podgoretsky   (2007-10-11 23:10:00) [11]

> Galinka  (11.10.2007 23:06:31)  [31]

Не знаю как в данном случае, возможно и нельзя, поскольку может получиться

*array
S1
*array
S2
...
array
Sn

В этом случае не возможно, о перемещении и речи быть не может.


Galinka   (2007-10-11 23:06:00) [12]

Anatoly Podgoretsky ©   (11.10.07 22:46) [28]

так я подозреваю, что чтобы объединить, то надо знать адрес и размер. А как их запомнить? Особенно при числе перераспределений в 60 000 операций. Серьезно спрашиваю.

Здесь я вижу только одну неясность. Мало что массив нарастает, так еще и размер одного из полей строго не определен.

Это как раз то же самое, когда я делала парсер текстового файла, в котором содержатся числа определенного типа. Файл со строками переменной длины и количество строк в файле заранее неизвестно. Т.е. надо перераспределять при каждом считывании числа, и при каждом переходе на новую строку. И тогда я решила сначала "оценить" размер: посчитать количество строк и количество "слов" в каждой строке. Потом один раз выделяем память под количество строк, а для каждого указателя вновь "объявленного" массива один раз перераспределяем по количеству "слов".


Германн   (2007-10-11 22:57:00) [13]

Не. Это было давно. Моя натура не позволит потратить мою злость на тех, кто давно уже молчит.
P.S. А фрагментация есть всегда!


Galinka   (2007-10-11 22:47:00) [14]

Германн ©   (11.10.07 22:42) [27]

http://www.delphimaster.ru/cgi-bin/forum.pl?id=1190111223&n=3

вот тут ))


Anatoly Podgoretsky   (2007-10-11 22:46:00) [15]

> Galinka  (11.10.2007 22:34:26)  [26]

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

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

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


Германн   (2007-10-11 22:42:00) [16]


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

Это где сказали? Приведи ссылку. Я сегодня злой. :-)


Galinka   (2007-10-11 22:34:00) [17]

Anatoly Podgoretsky ©   (11.10.07 22:28) [25]

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

Кстати на Королевстве довольно доходчивая статья по этому поводу. Там автор даже свой менеджер памяти писал. Который и собирал освобожденные участки памяти, объединял и проверял, можно ли поместить новый объем в уже освобожденные участки памяти.


Anatoly Podgoretsky   (2007-10-11 22:28:00) [18]

> Galinka  (11.10.2007 22:26:24)  [24]

Именно это и происходит.
Не понимаю, почему нет функции принудительной дефрагментации памяти, не помешала бы.


Galinka   (2007-10-11 22:26:00) [19]

А вот мне в соседней ветке говорили про фрагментацию. Может в том же дело? Или в дельфи фрагментации не бывает. Т.е. каждый раз при перераспределении памяти под массив, все время выделяется все большее место, а старое уже не используется. Тогда понятна лавинообразность. Т.е. при втором выделении памяти объем под первую запись не освободится, а под две выделится новые два размера, и так по нарастающей (( Произойдет утечка памяти. поэтому вместо прикидочных 5 программа занимает 250.


vpbar   (2007-10-11 20:10:00) [20]

гы. Вообщем интуитивно я угадал. Да и перераспределять память при каждом добавлении долго


Страницы:   1 2 3

Наверх

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