64-битные большие mallocs

В чем причины неудачи malloc(), особенно в 64 бит?

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

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

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

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

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

Ответы

Ответ 1

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

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

Ответ 2

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

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

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

Ответ 3

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

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

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

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

Ответ 4

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

Ответ 5

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

СЛОВО К WYSE или * СОВЕТЫ ДЛЯ СОХРАНЕНИЯ ВАШЕГО ЗДОРОВЬЯ HEAP *

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

void *foo = malloc(SIZE);

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

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

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

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

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

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

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

Ответ 6

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

Ответ 7

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

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

Ответ 8

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

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

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

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

Еще один предел, который определенно существует в Windows, - это предел фиксации. Дополнительная информация по этому вопросу:

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

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

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

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

Ответ 9

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

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

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

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

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

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

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