64-битные большие malloc - PullRequest
16 голосов
/ 07 мая 2009

Каковы причины сбоя malloc (), особенно в 64-битной версии?

Моя конкретная проблема - попытка распределить огромный объем 10 ГБ ОЗУ в 64-битной системе. Машина имеет 12 ГБ оперативной памяти и 32 ГБ подкачки. Да, malloc является экстремальным, но с чем это может быть проблемой? Это в Windows XP64 с компиляторами Intel и MSFT. Malloc иногда успешно, иногда нет, около 50%. Mallocs 8GB всегда работают, malloc 20GB всегда терпят неудачу. Если malloc завершается неудачно, повторные запросы не будут работать, если я не выйду из процесса и не начну новый процесс заново (в этом случае результат будет на 50% успешным). Другие большие приложения не работают. Это происходит даже сразу после новой перезагрузки.

Я мог бы представить, что malloc завершится с ошибкой в ​​32 битах, если вы израсходовали 32 (или 31) бит доступного адресного пространства, так что нет диапазона адресов, достаточно большого для назначения вашему запросу.

Я мог бы также представить сбой malloc, если вы израсходовали физическую RAM и пространство подкачки жесткого диска. Это не так для меня.

Но почему иначе malloc может выйти из строя? Я не могу думать о других причинах.

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

Ответы [ 9 ]

8 голосов
/ 07 мая 2009

malloc пытается выделить непрерывный диапазон памяти, и это первоначально будет в реальной памяти просто из-за того, как работает память подкачки (по крайней мере, насколько я помню). Вполне возможно, что ваша ОС иногда не может найти непрерывный блок в 10 ГБ памяти и все равно оставляет все процессы, требующие реальной памяти, в ОЗУ одновременно (в этот момент ваш malloc не будет работать).

Вам действительно требуется 10 ГБ непрерывной памяти, или вы сможете обернуть класс / структуру хранения вокруг нескольких меньших блоков и использовать вместо этого свою память в виде кусков? Это ослабляет огромное непрерывное требование и также должно позволить вашей программе использовать файл подкачки для менее используемых кусков.

5 голосов
/ 07 мая 2009

Вы пытались использовать VirtualAlloc() и VirtualFree() напрямую? Это может помочь изолировать проблему.

  • Вы будете обходить кучу времени выполнения C и кучу NT.
  • Вы можете зарезервировать виртуальное адресное пространство и затем зафиксировать его. Это скажет вам, какая операция не удалась.

Если резервирование виртуального адресного пространства завершается неудачно (даже если это не так, судя по тому, что вы сказали), Sysinternals VMMap может помочь объяснить, почему Включите «Показывать свободные регионы», чтобы посмотреть, как фрагментируется свободное виртуальное адресное пространство.

3 голосов
/ 07 мая 2009

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

Если сбой malloc на 20 ГБ, успешны ли четыре malloc на 5 ГБ? Если это так, то это проблема смежного пространства.

Проверяли ли вы переключатели компилятора на предмет ограничения общего размера кучи или наибольшего размера блока кучи?

Вы пытались написать программу, которая объявляет статическую переменную необходимого размера? Если это работает, вы можете реализовать свою собственную кучу с большими malloc в этом пространстве.

2 голосов
/ 07 мая 2009

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

1 голос
/ 24 апреля 2013

Проблема заключается в том, что Visual Studio не определяет WIN64 при компиляции 64-разрядного приложения, обычно он по-прежнему сохраняет WIN32, что неправильно для 64-разрядных приложений. Это тогда заставляет во время выполнения использовать 32-битное значение, когда определено _HEAP_MAXREQ, поэтому все большие malloc() завершатся с ошибкой. Если вы измените ваш проект (в свойствах проекта, предварительно обработанных определениях) на WIN64, то у очень большого malloc() не должно возникнуть никаких проблем.

1 голос
/ 02 июня 2009

Вот официальный источник, в котором указано, что максимальный размер запроса кучи определяется вашей связанной библиотекой CRT (кроме вашего предыдущего кода с целочисленными переполнениями, равными 0, поэтому вы не вернули NULL) (_HEAP_MAXREQ).

http://msdn.microsoft.com/en-us/library/6ewkz86d.aspx

Ознакомьтесь с моим ответом здесь для больших оконных окон, я включаю ссылку на MS-документ о продвижении модели памяти в Vista / 2008.

Короче говоря, стандартная CRT не поддерживает даже для собственного 64-битного процесса любой размер кучи, превышающий 4 ГБ. У вас есть для использования VirtualAlloc * или CreateFileMapping или некоторых других аналогов.

О, я также заметил, что вы утверждаете, что ваши большие ассигнования действительно успешны, это на самом деле неправильно, вы неправильно интерпретируете malloc (0x200000000); (это 8gb в шестнадцатеричном формате), происходит то, что вы запрашиваете 0-байтовое распределение из-за приведения или какого-либо другого эффекта вашего тестового жгута, вы совершенно определенно не наблюдаете что-либо больше, чем фиксируемая куча 0xfffff000 байтов, это просто вы видите целочисленные переполнения при приведении.

СЛОВО ДЛЯ ПОТЕРЯ или * СОВЕТЫ, ЧТОБЫ СПАСИТЬ ВАШУ КУХНУЮ САНИТИЮ *

ТОЛЬКО СПОСОБ ВЫДЕЛЕНИЯ ПАМЯТИ С MALLOC (ИЛИ ДРУГОЙ ДИНАМИЧЕСКОЙ ЗАПРОС)

void *foo = malloc(SIZE);

ЗНАЧЕНИЕ ДИНАМИЧЕСКОГО ЗАПРОСА ПАМЯТИ НИКОГДА НЕ ДОЛЖНО (Я НЕ МОГУ УКАЗАТЬ ЭТОГО ДОСТАТОЧНО) РАСЧЕТАТЬСЯ В "()" ПАРЕНСКОМ ЗАПРОСЕ

mytype *foo = (mytype *) malloc(sizeof(mytype) * 2);

Опасность заключается в переполнении integer .

Это всегда кодировка ОШИБКА для выполнения арифметики во время вызова, вы ДОЛЖНЫ ВСЕГДА вычислять ОБЩУЮ СУММУ данных, которые должны быть запрошены до оператор, который оценивает запрос.

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

Чтобы использовать то, что мы просили, мы должны знать, насколько оно велико? (например, количество массивов, размер шрифта и т. д.).

Это будет означать, что если мы когда-либо увидим какую-либо арифметику внутри () запроса ресурса, это будет ошибкой, поскольку мы ДОЛЖНЫ дублируем этот код снова в порядке использовать эти данные соответствующим образом.

0 голосов
/ 21 марта 2015

Мне показался интересным вопрос, поэтому я попытался исследовать его с теоретической точки зрения:

В 64-битном (на самом деле 48-битном использовании из-за ограничений чипа и менее (44-битном?) Из-за ограничений ОС) вы, безусловно, должны не быть ограничены фрагментацией виртуальной памяти, т.е. смежного виртуального адресного пространства. Причина в том, что виртуального адресного пространства так много, что его практически невозможно исчерпать.

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

Итак, вы должны столкнуться с чем-то другим : о. некоторые другие ограничения, которые применяются к виртуальной памяти.

Еще одно ограничение, которое определенно существует в Windows, - это предел фиксации. Подробнее об этом:

http://blogs.technet.com/b/markrussinovich/archive/2008/11/17/3155406.aspx

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

Подробнее о том, как на самом деле работают таблицы страниц для преобразования виртуальных адресов, можно прочитать здесь:

http://en.wikipedia.org/wiki/Memory_management_unit

0 голосов
/ 07 мая 2009

Это, скорее всего, фрагментация. Для простоты, давайте использовать пример.

Память состоит из одного модуля 12 КБ. Эта память организована в блоки по 1 КБ в MMU. Итак, у вас есть 12 блоков по 1 килобайту. Ваша ОС использует 100 байт, но это в основном код, управляющий таблицами страниц. Таким образом, вы не можете поменять его. Затем все ваши приложения используют по 100 байт.

Теперь, когда запущена только ваша ОС и приложение (200 байт), вы уже использовали бы 200 байт памяти (занимающих блоки 2 КБ). Оставив ровно 10кб доступно для malloc().

Теперь вы начали с malloc() пары буферов - A (900 байт), B (200 байт). Затем вы освобождаете A. Теперь у вас есть 9.8kb свободных (несмежных). Итак, вы пытаетесь malloc() C (9kb). Внезапно вы терпите неудачу.

У вас есть 8.9k смежных в хвостовой части и 0.9k в передней части. Вы не можете повторно отобразить первый блок до конца, потому что B растягивается на первый 1k и второй 1k блоков.

Вы все еще можете malloc() один блок 8 КБ.

Конечно, этот пример немного надуман, но надеюсь, что он поможет.

0 голосов
/ 07 мая 2009

Но почему еще может выйти из строя malloc? я не могу думать о других причинах

Как неявно заявлено ранее несколько раз из-за фрагментации памяти

...