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

Вниз

Ошибка "Out of memory"   Найти похожие ветки 

 
TrueCoder   (2005-06-01 03:29) [0]

Так как Delphi пользуюсь нечасто, возникла проблема, решить которую сам пока не могу. Поиск тоже ничего что-то не дал..

Объявлен тип, например: type TValues = array [0..8] of Variant;
Объявлен массив:   Values: array of TValues;

Имеется счетчик элементов массива Values = Count: Integer.
Изначально массив пуст, значит Count=0.
Новые элементы массива Values добавляются в цикле по одному через SetLength(Values,Count+1). И все работает, до тех пор пока размер памяти, занимаемый приложением, не достигает 128 мегабайт. После чего, возникает ошибка "Out of memory".
Причем, размер оперативки в компе - 1 Гб, не говоря уже о виртуальной памяти. В момент возникновения ошибки оперативки свободно около 500 Мб. Выгрузка вообще всех других приложений тоже не помогает.

Вопрос - отчего возникает такая ошибка? Может я чего не понимаю в механизме выделения памяти? В хелпе имеется такая фраза по SetLength - "If there is not enough memory available to reallocate the variable, SetLength raises an EOutOfMemory exception", что у меня, похоже, и происходит. Но почему?!

ОС - WinXP Home, сама Delphi - семерка. Благодарю за любую помощь.


 
Defunct ©   (2005-06-01 04:35) [1]

Попробуйте заменить массив на TList, там SetLength используется немного не так как у вас.


 
Digitman ©   (2005-06-01 08:45) [2]

при очередном запросе Вorland Мemory Мanager"а на перераспределение памяти система не обнаружила в АП тек.процесса свободного региона затребованного размера и вернула отказ, о чем ВММ любезно и сообщил соотв.исключением

скорей всего АП процесса на момент отказа было сильно фрагментировано по причине предыдущих массированных "мелких" реаллокаций, инициированных вызовами SetLength()


 
ANB ©   (2005-06-01 08:57) [3]


> Digitman ©   (01.06.05 08:45) [2]
Имхо. Очень странно. Занято 128М. Дефрагментация, понятно, но у процесса 4Г адресного пространства. Как может не хватить ? Может проблема в стеке ?


 
Anatoly Podgoretsky ©   (2005-06-01 09:06) [4]

TrueCoder   (01.06.05 03:29)  
Где код?


 
Bel ©   (2005-06-01 09:09) [5]

> но у процесса 4Г адресного пространства

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


 
ANB ©   (2005-06-01 09:16) [6]


> Причем, учти, из них половина отдана под ядро
- Ну 2Г. Ну 500М накладные расходы (сроду столько не было). 1,5Г забить одним массивом ?
В принципе хороший совет - сначала определить максимальную длинну, а потом писать в массив, но он не всегда выполним. Например, я не всегда знаю заранее размер массива. В своем проекте у меня много мест, где длинна приращивается на 1, но на такую ошибку ни разу не нарывался. Ресурсы раз кончаться начали, но это я компонент кривой заюзал. Поменял на стандартный - ошибка ушла.


 
Bel ©   (2005-06-01 09:21) [7]

Дык, массив то должен занимать непрерывный участок памяти. Памяти то может быть и 1.5Г свободно, а вот непрерывного участка размером, допустим, 10М может не быть. Поэтому в данном случае уже предложили решение - использовать TList.


 
evvcom ©   (2005-06-01 09:22) [8]

Вообще подход действительно неверный. Реаллокэйтить такие массивы - это встретиться с жуткими тормозами из-за постоянного копирования "полезной" информации. Лучше использовать TList или цепочки.


 
Digitman ©   (2005-06-01 09:22) [9]


> ANB ©   (01.06.05 08:57) [3]


> Занято 128М


эта цифирь не имеет отношения к ВАП процесса


 
ЮЮ ©   (2005-06-01 09:32) [10]

Если человеку не нужны механизмы TList, то достаточно увеличивать длину массива так, как это сделано в TList.Grow, тем более, что количество действительных элементов массива он и так подсчитывает в Count


 
ANB ©   (2005-06-01 09:32) [11]


> эта цифирь не имеет отношения к ВАП процесса
- согласен. Но не полностью. Чем больше эта цифра, тем больше вероятности нарваться на конец памяти. Приплюсовать сюда дефрагментацию . . . Кстати, автор не дал код и не сообщил максимальную длинну своего массива.


 
Mx ©   (2005-06-01 09:46) [12]

Возник вопрос: а чем же TList лучше? В не тоже есть Realloc"и, хоть и реже вызываемые, но в итоге точно также потребуется большой объем памяти (правда Pointer меньше чем Variant, но это лишь вопрос количества элементов).


 
Думкин ©   (2005-06-01 09:48) [13]

> [12] Mx ©   (01.06.05 09:46)

хоть и реже вызываемые, - ты сказал(с)


 
Mx ©   (2005-06-01 09:55) [14]


> Думкин ©   (01.06.05 09:48) [13]

Вроде ошибка: Out of memory, а не Unable to Realloc? Не хватает, как я понял, объема, причем здесь количество реалокейтов?


 
ANB ©   (2005-06-01 09:56) [15]

Без кода и поллитры не разберемся.


 
Думкин ©   (2005-06-01 10:05) [16]

> [14] Mx ©   (01.06.05 09:55)

смотрим [2] Digitman ©   (01.06.05 08:45) - задумываемся.


 
Mx ©   (2005-06-01 10:13) [17]


> Думкин ©   (01.06.05 10:05) [16]

Я уже читал. Но: Дык, массив то должен занимать непрерывный участок памяти. Памяти то может быть и 1.5Г свободно, а вот непрерывного участка размером, допустим, 10М может не быть. Поэтому в данном случае уже предложили решение - использовать TList. © Bel. Ну допустим, Realloc не смог выделить память в текущем месте, я так понимаю занимаемую память он освободит, и будет выделен новый - бОльший участок в другом месте. Ну так и причем здесь количество Realloc"ов? Или он работает только "по возрастанию"? Т.е. в конце концов может "упереться в вершину" (извините, за образность)?


 
evvcom ©   (2005-06-01 10:17) [18]


> Mx ©   (01.06.05 10:13) [17]

Каков характер твоих данных? Необходим ли тебе доступ к произвольному элементу этого массива в любой момент времени? Или эти данные обрабатываются строго по порядку (сверху вниз или снизу вверх, не важно)?


 
Mx ©   (2005-06-01 10:20) [19]

У меня данных нет, почитал ветку и кое чего не понял, вот и спросил... вдогонку.


 
evvcom ©   (2005-06-01 10:24) [20]

Да, действительно... [18] должно быть адресовано TrueCoder


 
Mx ©   (2005-06-01 10:31) [21]

Продолжаю. Допустим у нас 20 ячеек памяти, каждая пятая занята. Тогда какая разница, что я "дойду" до необходимости выделения 6 смежных ячеек, а что я их сразу запрошу? Свободных то все-равно нет. Это я имел ввиду в вопросе.


 
Digitman ©   (2005-06-01 10:33) [22]


> Mx ©   (01.06.05 09:55) [14]
> Вроде ошибка: Out of memory, а не Unable to Realloc? Не
> хватает, как я понял, объема, причем здесь количество реалокейтов?


найти грабли достаточно легко - перехватываем точки входа в VirtualAlloc и LocalAlloc, протоколируем отказы этих вызовов

если отказал VirtualAlloc, грабли в дефрагментации и/или отсутствии региона нужного размера

если LocalAlloc, грабли - в исчерпании кучи


 
Mx ©   (2005-06-01 10:38) [23]

У меня примера нет. Может автор проверит?


 
Digitman ©   (2005-06-01 10:41) [24]

getmem.inc наглядно показывает grow-механизм работы ВММ - сначала в АП резервируется регион, а затем идет запрос к дифолтной куче

а в целом грабли даже и искать не нужно

лезем в sysutils.pas, видим там

EOutOfMemory = class(EHeapException);

т.е. отказ вернула ф-ция LocalAlloc из-за исчерпания кучи


 
Mx ©   (2005-06-01 10:43) [25]

Как на это влияет число вызовов ReallocMem?


 
Digitman ©   (2005-06-01 11:00) [26]

вот так и влияет

AddBlockAfter() вызывает LocalAlloc() посредством GetBlockDesc(), запрашивая из кучи блок памяти, а DeleteBlock() назад в кучу его не отдает


 
Mx ©   (2005-06-01 11:32) [27]

Все-равно не понимаю. Если я запрошу блок вначале 1*Integer, потом 2*Integer, потом 3*Integer. А если запрошу сразу 3*Integer, то что выиграю? И в том и в другом случае я запрашиваю 3*Integer ячеек. А! В первом случае получается 6*Integer, да? Правильно понял? Т.е., высвобождая предыдущий блок, ReallocMem не далает эти ячейки доступными для следующего Realloc"а?


 
Digitman ©   (2005-06-01 11:58) [28]


> Mx ©   (01.06.05 11:32) [27]


> Если я запрошу блок вначале 1*Integer, потом 2*Integer,
> потом 3*Integer.


.. то потенциально возможны три вызова LocalAlloc() при ни одном LocalFree()


А если запрошу сразу 3*Integer, то что
> выиграю?


при этом будет не более чем один LocalAlloc()

ВММ оперирует понятием и механизмом "блок"

у каждого блока есть упр.структура для организации связного двунапр.списка из блоков, хранения атрибутов ("свободен", "занят"), адреса, где размещены собственно данные блока, если блок занят, и размера.

когда прикл.код обращается к ВММ за выделением памяти, ВММ ищет первый своб.блок минимально подходящего размера и помечает его как "занятый", манипулируя далее с соотв.регионами в АП процесса (1)

если своб.блок не найден, ВММ запрашивает из кучи фрагмент размера равного заголовку блока и затем выполняет (1)

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

реаллокация у ВММ - более сложный алгоритм (с т.з. манипуляций регионами), но сводится он опять же к аллокации нового и деаллокации прежнего блока

ну это так, сильно упрощенная схема ...
детали же реально происходящего - в getmem.inc


 
Mx ©   (2005-06-01 12:35) [29]

Как все запушено... Спасибо, на досуге повнимательнее почитаю getmem.inc.


 
TrueCoder   (2005-06-01 16:38) [30]

Большое спасибо всем ответившим. Даже не ожидал, что моя проблема поднимет такую дискуссию. Есть о чем подумать.
Трезвую мысль высказал Digitman: "Cкорей всего АП процесса на момент отказа было сильно фрагментировано по причине предыдущих массированных мелких реаллокаций, инициированных вызовами SetLength()". Интуитивно чувствую, что истина где-то тут.
Код привести, к сожалению, не могу, запутанный он. Там долго и нудно обрабатываются Variant-ячейки таблицы ExpressQuantumGrid (штука классная, но сложнаяяяя..). Исключение возникает при обработке порции порядка 5 тысяч записей, но записи большие, в каждой по 50 полей, большинство текстовые, разной длины, поэтому я даже затруднюсь сказать максимальный размер массива.

Главное, что я понял - ошибка где-то в самом коде. Точнее, даже не в коде, а в самом принципе использования памяти - не любит ОНО многочисленных мелких реалокаций. Буду думать, может TList прикрутить получится.

Насторожило то, что исключение возникает при объеме памяти, занимаемого приложением, в 128 Мб. По неопытности и подумал, может по умолчанию при компиляции какие ограничения существуют. А нет, чуда не произошло. :)


 
Digitman ©   (2005-06-01 17:05) [31]


> TrueCoder   (01.06.05 16:38) [30]


> Трезвую мысль высказал Digitman


да ну нет)... на более пристальный взгляд она не такая уж и трезвая оказалась ..

EOutOfMemory = class(EHeapException);

т.е. отказ этот вызван исчерпанием хипа, а с хипом работает LocalAlloc, а LocalAlloc вызывается для аллокации памяти под заголовок блока, а не под сами данные, на которые занятый блок ссылается

отсюда и вывод напрашивается - с дефрагментацией АП еще бабушка надвое сказала, а вот хип опустошен тобой множеством реаллокаций по самое нехочу


> Буду думать, может TList прикрутить получится


ТЛист пользует тот же менеджер памяти, разница лишь в grow-алгоритме : ТЛист при добавлении эл-та списка запрашивает память сразу под несколько (не помню сколько - см. код класса) элементов, т.о. минимизируя число вновь создаваемых блоков и, соответственно, новых аллокациий в хипе

рано или поздно и с ТЛист можно нарваться на ту же ситуацию


> Насторожило то, что исключение возникает при объеме памяти,
> занимаемого приложением, в 128 Мб


а ты не гадай !
ты проверь или отвергни предположение насчет хипа ..

сразу после старта приложения запроси у системы хэндл дифолтного хипа своего процесса (GetProcessHeap) и тут же просканируй хип (HeapWalk)

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


 
Anatoly Podgoretsky ©   (2005-06-01 20:40) [32]

Digitman ©   (01.06.05 17:05) [31]
ТЛист пользует тот же менеджер памяти, разница лишь в grow-алгоритме : ТЛист при добавлении эл-та списка запрашивает память сразу под несколько (не помню сколько - см. код класса) элементов, т.о. минимизируя число вновь создаваемых блоков и, соответственно, новых аллокациий в хипе

И не запомнишь, у него адаптирующий алгоритм, с переменным количеством сколько. Вроде бы каждое увеличение на 25% от текущего размера блока, чем больше запрашиваешь, чем больше получаешь.


 
Defunct ©   (2005-06-02 01:21) [33]

Anatoly Podgoretsky ©   (01.06.05 20:40) [32]
> разница лишь в grow-алгоритме

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


 
TrueCoder   (2005-06-03 23:39) [34]

Наверное, никому уже не интересно, но все же расскажу. Анализом кода удалось найти возможность заранее вычислять размер будущего массива, поэтому число реалокаций с 5 тысяч удалось снизить до двух. И проблема отпала сама собой.
Конечно, текущее решение значительно более красивое, как с точки зрения чистого кодинга, так и с точки зрения полученного удовлетворения от красивого решения проблемы :), но, все же, осталось некоторое удивление. Разве, стабильно работающая система (я о менеджере памяти Delphi и о самой операционке) не должны стабильно работать и при условии пусть некрасивых, но все же верных решений. Таких как 5 тысяч мелких реалокаций памяти у меня ранее. Что-то все же неладно в данном королевстве..
Интерес остался, конечно, исключительно чисто теоретический, проверять почему не работало раньше банально нет времени.


 
Defunct ©   (2005-06-04 02:53) [35]

> TrueCoder

Проблема фрагментации - вечная проблема. Менеджер памяти пойдет в ущер скорости исполения, если будет заниматься постоянной дефрагментацией.
Поэтому делайте всегда всё красиво.



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

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

Наверх





Память: 0.56 MB
Время: 0.042 c
1-1118260453
Gopher
2005-06-08 23:54
2005.06.29
Синтаксис


3-1116565491
Peter_cc
2005-05-20 09:04
2005.06.29
тип поля DATE


14-1117442333
Alexone
2005-05-30 12:38
2005.06.29
Тестирование программы


4-1114793145
Grell
2005-04-29 20:45
2005.06.29
Изменение BorderStyle


3-1116437770
another
2005-05-18 21:36
2005.06.29
движок базы данных





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