EOutOfMemory

Модераторы: Duncon, Naeel Maqsudov, Игорь Акопян, Хыиуду

Alexander83
Сообщения: 2
Зарегистрирован: 07 апр 2005, 17:44

При попытке

Код: Выделить всё

var a : array of byte;
...
SetLength (a, 1000000000);// хочу аллокейтить 1Гб
выдается ошибка EOutOfMemory, хотя в на самом деле памяти хватает - файл подкачки 4Гб (из них 3Гб точно всегда свободно). Помогите разобраться в чем дело.
Eugie
Сообщения: 708
Зарегистрирован: 17 фев 2004, 23:59
Откуда: SPb

Не все так просто. Файл подкачки - общий ресурс, используется совместно всеми процессами в системе. В нем и ОЗУ физически размещаются страницы памяти. Отдельный процесс имеет в своем распоряжении 2 Гб виртуальной памяти (еще 2 резервируются под системные нужды), т.е. потециально как бы может запросить до 2 гигов. Но реально действуют ограничения: а) часть виртуального адресного пространства процесса уже занята под код, данные и ресурсы; б) оставшуюся часть можно зарезервировать, но чтобы реально выделить (commit, фиксировать), что делают функции работы с дин.памятью (а SetLength неявно размещает именно блок из кучи), нужно, чтобы в ВАП был свободный непрерывный блок требуемого размера. Т.е. доп.ограничение возникает из-за фрагментации, а этот фактор трудно контролировать.
Аватара пользователя
LAngel
Сообщения: 277
Зарегистрирован: 30 мар 2005, 08:19
Откуда: Ульяновск
Контактная информация:

можно обмануть другим способом:

Код: Выделить всё

var a: PChar;
               b: Byte;
GetMem(a, 1000000000);
A[1234] := chr(b);  // работает с элементами таким образом.
b := Byte(a[1234]); // и таким :)
...
FreeMem(a);
С уважением, Lost Angel...
Аватара пользователя
Naeel Maqsudov
Сообщения: 2570
Зарегистрирован: 20 фев 2004, 19:17
Откуда: Moscow, Russia
Контактная информация:

Куча куче рознь.
Можно использовать функции встроенного менеждера памяти (GetMem, например), которые работают с локальной кучей, а можно и самому (если это оправданно) с памятью поработать:
LocalAlloc/GlobalAlloc и остальные функции из этой группы (см справку WinAPI)

И не забывайте все места, где потенциально возможна ошибка (а любое выдылыние памяти, причем не зависимо от размера это однозначно одно из таковых!) заключать в try...except...end.
Eugie
Сообщения: 708
Зарегистрирован: 17 фев 2004, 23:59
Откуда: SPb

LAngel, твой пример ничего не доказывает. Более того, я на 98% уверен, что и SetLength (a, 1000000000) у тебя отработает. Это говорит только о том, что в данный момент на твоей системе условия позволяют выделить столько памяти. А в другой ситуации (ОЗУ, файл подкачки, особенности твоего процесса итд) получишь EOutOfMemory.
Аватара пользователя
LAngel
Сообщения: 277
Зарегистрирован: 30 мар 2005, 08:19
Откуда: Ульяновск
Контактная информация:

Eugie, и SetLength работает :)
С уважением, Lost Angel...
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

И не забывайте все места, где потенциально возможна ошибка (а любое выдылыние памяти, причем не зависимо от размера это однозначно одно из таковых!) заключать в try...except...end.
Герб Саттер (недавно тут читал) пишет это уже не актуально.
Во первых на современных ОС любое выделение памати зачастую заведомо успешно. При этом реального выделения памяти не происходит, а выполняется резервирование страниц и возвращается фальшивый указатель. При обращениии к памяти, на которую указывает этот указатель возникает страничное исключение, ОС его перехватывает и подсовывает свежевыделенную страницу виртуальной памяти.
Во вторых если памяти действительно не хватает, то пыжиться, чтобы красиво выйти по моему излишне. Вернула функция резервирования нулевой указатель. Что делать? Кидать екзепшен? Его кидание потребует байтиков на куче. Чтобы деинициализировать и сохранить все пользовательские данные тоже нужна будет память. Вылет с ошибкой записи по адресу ноль тут ни чем не хуже всех остальных вариантов.
2B OR NOT(2B) = FF
Аватара пользователя
LAngel
Сообщения: 277
Зарегистрирован: 30 мар 2005, 08:19
Откуда: Ульяновск
Контактная информация:

И что самое интересное, SetLength работает довольно "долго", а GetMem отрабатывает сразу...
С уважением, Lost Angel...
Eugie
Сообщения: 708
Зарегистрирован: 17 фев 2004, 23:59
Откуда: SPb

на современных ОС любое выделение памати зачастую заведомо успешно.
По крайней мере, в Виндах это не так. Если запросишь слишком много = получишь кукиш, а не указатель :) Насчет ненужности try..except ты загнул. Если есть возможность откатиться без фатального вылета - ей надо пользоваться по-любому, хотя бы для уяснения причин ошибки. Много толку будет, если твоя программа молча упадет?

LAngel, SetLength() кроме собственно размещения памяти еще и записывает в нее несколько байтов служебной информации. А ОС физически загружает страницы только при первом обращении к ним. Вот и ответ.
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

Если есть возможность откатиться без фатального вылета - ей надо пользоваться по-любому, хотя бы для уяснения причин ошибки
Корректно обработать ошибку выделения памяти можно только в институтской лабе, которая просит пользователя вести размер массива, что-то делает с этим массивом а потом выходит. В серьезной программе если эта ошибка и возникает, то всегда в деликатный момент, когда отваливаться нельзя. За последние несколько лет я сталкивался с OutOfMemory только один раз в Java (там всегда есть потолок на размер хипа). Дык к тому моменту, когда программа понимала что что-то не так, логгер (log4j) уже был мертв. И умирала прога по тихому. Писали эту программу очень аккуратные люди, обработка ошибок была организована грамотно. log4j тоже писали грамотные люди.

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

В программе типа сервера БД я бы с самого начала выделял сколько памяти мне надо (с помощью VirtualAlloc), а потом только ее и использовал. Кроме того, имел бы аварийный спул памяти. Если я не имею такую инфраструктуру в обычных программах, то зачем пыжиться?

Насчет отката... Если клиент умер, то сервер откатит текущую транзанкцию. MySQL не откатит, но любители MySQL вообще ошибки не проверяют. А умер клиент скорее всего не от OutOfMemory, а от того что его прибил пользователь в Task Manager, поскольку пользователя задолбал своп.
2B OR NOT(2B) = FF
Ответить