2009-10-24 2 views
0

У меня есть следующая проблема (?):Из памяти проблемы на Win32 (по сравнению с Linux)

Программа запуска на компьютере ОС Windows (32-разрядная, 3.1Gb памяти, как VC++ 2008 и MinGW скомпилированный код) завершается с ошибкой bad_alloc (после выделения около 1,2 ГБ, исключение возникает при попытке выделить вектор из 9 миллионов удвоений, т. е. около 75 МБ), при этом имеется достаточное количество ОЗУ (по крайней мере, согласно диспетчеру задач).

Те же программы, которые запускаются на машинах Linux (32-разрядная, 4 ГБ-памяти, 32-разрядная, 2 ГБ-память), отлично работают с максимальной памятью памяти около 1,6 ГБ. Интересно, что код win32, созданный mingw, запущенным на 4Gb-машине linux под вином, также терпит неудачу с bad_alloc, хотя и в другом (более позднем) месте, а затем при запуске под окнами ...

Каковы возможные проблемы?

  • Куча фрагментации? (Как я могу это знать? Как это можно решить?)
  • Куча коррупции? (Я запускаю код с включенным параметром pageheap.exe без сообщений об ошибках, реализованный векторный доступ с проверкой границ - снова нет ошибок, код по существу свободен от указателей, используются только std::vector s и std::list. Запуск программы под Valgrind (memcheck) потребляет слишком много памяти и заканчивается преждевременно, но не находит ошибок)
  • Недостаточно памяти ??? (Там должно быть достаточно памяти)

Кроме того, что может быть причиной того, что версия окна терпит неудачу в то время как версия Linux работает (и даже на машинах с меньшим объемом памяти)? (Также обратите внимание, что флаг линкера/LARGEADDRESSAWARE используется с VC + 2008, если это может иметь никакого эффекта)

Любые идеи были бы оценены, я в моем конце остроумия с этим ... :-(

+0

Я заметил, что на самом деле я постоянно изменял размеры векторов, которые могут привести к фрагментации. Попытался это исправить, но он, похоже, не имеет желаемого эффекта, но я, возможно, что-то пропустил. Конечно, есть что-то для исследования (например, выход sysinternals). Вернусь, когда узнаю больше ... –

+0

Оказывается, что фрагментация кучи была виновником. Я смог устранить большую часть изменения размера вектора. Однако проблема все еще оставалась, потому что построение большого (около 9 миллионов строк) вектора 'std :: list's немедленно приводило программу вниз. Я предполагаю, что мне придется реализовать пользовательский распределитель для списков (я об этом мало знаю) или переключиться на реализацию списков как массивы фиксированного размера (мои списки небольшие, поэтому я не потеряю много памяти) , Интересно, что при компиляции с помощью mingw программа теперь укладывается в 2GB, а с VC - нет. –

ответ

0

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

Я испытал это только с VC, а не с MinGW, но тогда я еще не проверил, это все еще может объяснить проблему.

+0

Пробовал с выпуском, выпускать с отладочной информацией, отлаживать безрезультатно –

+0

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

+0

@Martin, только что я несколько раз сталкивался с двойными массивами с Visual C, я не проверял тайны того, как Microsoft управляла им. Симптомы были точно такими же, и это очень быстрый тест, поэтому стоит упомянуть, даже если в конечном итоге это не сработало для OP в этом случае. – RedGlyph

5

Это не имеет никакого отношения к тому, сколько оперативной памяти в вашей системе. У вас закончилось виртуальное адресное пространство. Для 32-битного процесса ОС Windows вы получаете виртуальное адресное пространство 4 ГБ (независимо от того, сколько оперативной памяти вы используете) из 2 ГБ для пользовательского режима (3 ГБ в случае LARGEADDRESSAWARE) и 2 ГБ для ядра. Когда вы попытаетесь выделить память, используя новую, ОС попытается найти блок contiguos виртуальной памяти, который достаточно велик, чтобы удовлетворить запрос на распределение памяти. Если ваше виртуальное адресное пространство сильно фрагментировано или вы запрашиваете огромный блок памяти, тогда он не сможет выбросить исключение bad_alloc. Проверьте, сколько виртуальной памяти используется вашим процессом.

+0

Это тоже хороший момент, виртуальная память действительно имеет значение в этом случае. – RedGlyph

+0

Спасибо за ответ! Как узнать виртуальную память, которую использует этот процесс? Менеджер задач говорит, что программа использует 1.2Gb, когда она терпит неудачу. Это должно быть значительно ниже отметки 2 ГБ (и я связываюсь с LARGEADDRESSAWARE). В этот момент программа пытается выделить только около 75 Мб ... Есть ли способ узнать, сильно ли фрагментировано адресное пространство? Как его можно избежать? –

+0

Попробуйте включить столбец в диспетчере задач, который называется что-то вроде «виртуальной памяти» на XP и что-то вроде «private (?) Bytes» on vistas. Также рассмотрите возможность использования Process Explorer из sysinternals (AKA Rusinovich), который намного превосходит диспетчер задач. Что-то вроде «perfmon» (который, я считаю, поставляется с окнами), также может помочь вам понять, что именно происходит с памятью вашей машины. – Dmitry

2

С Windows XP x86 и настройками по умолчанию 1,2 ГБ - это все адресное пространство, которое вы оставили для своей кучи после того, как системные библиотеки, ваш код, стек и другие материалы получили свою долю. Обратите внимание, что для работы с файлом largeaddressaware необходимо загрузиться с флагом загрузки/3GB, чтобы попытаться предоставить вашему процессу до 3 ГБ. Флаг/3GB вызывает нестабильность во многих системах XP, поэтому по умолчанию он не включен.

Варианты сервера Windows x86 дают вам больше адресного пространства, как с использованием разделения 3GB/1GB, так и с помощью PAE, чтобы позволить вам использовать ваш полный 4 ГБ ОЗУ.

Linux x86 по умолчанию использует разделение по 3 ГБ/1 ГБ.

64-разрядная ОС даст вам больше адресного пространства, даже для 32-битного процесса.

+0

Хм, спасибо за объяснение с флагом загрузки/3GB, я не знал об этом. Кажется, что разделение 3/1 для Linux указывает, почему программа работает под Linux. –

0

Подробнее о виртуальной памяти: Приложение не работает, когда пытается выделить один массив размером 90 Мбайт, и не существует непрерывного пространства виртуальной памяти, где это может поместиться влево. Возможно, вы сможете немного продвинуться, если переключитесь на структуры данных, которые используют меньше памяти - возможно, какой-то класс, который приближается к огромному массиву, используя дерево, где все данные хранятся в 1 МБ (или около того) листовых узлах. Кроме того, при использовании C++ при выполнении огромного количества распределений это действительно помогает, если все эти большие распределения имеют одинаковый размер, это помогает повторно использовать память и значительно снижает фрагментацию.

Однако правильная вещь в конечном итоге - это просто перейти на 64-битную систему.

+0

Оказалось, что фрагментация кучи, вероятно, была проблемой. –