В чем различия между VirtualAlloc и HeapAlloc? - PullRequest
74 голосов
/ 16 мая 2009

Существует множество способов выделения памяти в среде Windows, например VirtualAlloc, HeapAlloc, malloc, new.

Итак, в чем разница между ними?

Ответы [ 6 ]

79 голосов
/ 16 мая 2009

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

VirtualAlloc

Низкоуровневый Windows API, который предоставляет множество опций, но в основном полезен для людей в довольно специфических ситуациях. Можно только выделить память в (редактировать: не 4 КБ) больших кусков. Есть ситуации, когда вам это нужно, но вы будете знать, когда окажетесь в одной из этих ситуаций. Одним из наиболее распространенных является случай, когда вам нужно совместно использовать память с другим процессом. Не используйте его для общего назначения памяти. Используйте VirtualFree для освобождения.

HeapAlloc

Выделяет любой объем памяти, который вы запрашиваете, не большими порциями, чем VirtualAlloc. HeapAlloc знает, когда ему нужно позвонить VirtualAlloc, и делает это автоматически. Как и malloc, но только для Windows и предоставляет еще несколько опций. Подходит для выделения общих фрагментов памяти. Некоторые API-интерфейсы Windows могут потребовать, чтобы вы использовали это для выделения памяти, которую вы им передаете, или используете его компаньон HeapFree для освобождения памяти, которую они вам возвращают.

таНос

C-способ выделения памяти. Предпочитайте это, если вы пишете на C, а не на C ++, и хотите, чтобы ваш код работал, например, на. Компьютеры Unix тоже, или кто-то конкретно говорит, что вам нужно его использовать. Не инициализирует память. Подходит для выделения общих фрагментов памяти, например HeapAlloc. Простой API. Используйте free для освобождения. Visual C ++ malloc вызывает HeapAlloc.

новый

C ++ способ выделения памяти. Предпочитайте это, если вы пишете на C ++. Он также помещает объект или объекты в выделенную память. Используйте delete для освобождения (или delete[] для массивов). new Visual Studio вызывает HeapAlloc, а затем, возможно, инициализирует объекты, в зависимости от того, как вы их называете.

В последних стандартах C ++ (C ++ 11 и выше), если вам приходится вручную использовать delete, вы делаете это неправильно и вместо этого должны использовать интеллектуальный указатель , такой как unique_ptr. Начиная с C ++ 14, то же самое можно сказать о new (заменяется такими функциями, как make_unique()).


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

28 голосов
/ 16 мая 2009

VirtualAlloc - это специализированное распределение системы виртуальной памяти (ВМ) ОС. Распределения в системе VM должны выполняться с гранулярностью распределения, которая (гранулярность распределения) зависит от архитектуры. Распределение в системе VM является одной из самых основных форм распределения памяти. Распределение виртуальных машин может принимать несколько форм, память не обязательно выделена или физически поддерживается в ОЗУ (хотя это может быть) Распределение виртуальных машин обычно является типом специального назначения , либо из-за того, что распределение имеет значение

  • быть очень большим,
  • необходимо отправить,
  • должен быть выровнен по определенному значению (из соображений производительности) или
  • звонящему не нужно использовать всю эту память сразу ...
  • и т.д ...

HeapAlloc по сути то, что malloc и new оба в конечном итоге называют. Он спроектирован так, чтобы быть очень быстрым и использоваться в различных сценариях общего назначения. Это «Куча» в классическом смысле. Кучи на самом деле настраиваются с помощью VirtualAlloc, который используется для первоначально резервного пространства выделения из ОС. После инициализации пространства с помощью VirtualAlloc различные таблицы, списки и другие структуры данных настраиваются для поддержки и управления работой HEAP. Некоторые из этих операций выполняются в форме динамического изменения размера (увеличения и уменьшения) кучи, адаптации кучи к конкретному использованию (частое распределение некоторого размера) и т. Д.

new и malloc несколько одинаковы, malloc по сути является точным вызовом HeapAlloc( heap-id-default ); new однако, может [дополнительно] настроить выделенную память для объектов C ++ . Для данного объекта C ++ будет хранить vtables в куче для каждого вызывающего. Эти таблицы являются перенаправлениями для выполнения и являются частью того, что дает С ++ такие ОО-характеристики, как наследование, перегрузка функций и т. Д. *

Некоторые другие распространенные методы выделения, такие как _alloca() и _malloca(), основаны на stack ; FileMappings действительно выделяются с VirtualAlloc и устанавливаются с определенными битовыми флагами, которые обозначают эти отображения как тип FILE.

В большинстве случаев вы должны распределять память таким образом, чтобы это соответствовало использованию этой памяти;). new в C ++, malloc для C, VirtualAlloc для массивных или IPC случаев.

*** Обратите внимание, что выделение большого объема памяти, сделанное HeapAlloc, на самом деле отправляется до VirtualAlloc после некоторого размера (пара сотен k или 16 МБ или что-то, что я забыл, но довольно большой :)).

*** РЕДАКТИРОВАТЬ Я кратко заметил об IPC и VirtualAlloc, есть также что-то очень аккуратное в связанном VirtualAlloc, которое никто из респондентов на этот вопрос не обсуждал.

VirtualAlloc Пример - это то, что один процесс может использовать для выделения памяти в адресном пространстве другого процесса. Чаще всего это используется в комбинации для получения удаленного выполнения в контексте другого процесса через CreateRemoteThread (аналогично CreateThread, поток просто запускается в другом процессе).

21 голосов
/ 18 мая 2017

Очень важно понимать различие между API-интерфейсами выделения памяти (в Windows), если вы планируете использовать язык, требующий управления памятью (например, C или C ++). И лучший способ проиллюстрировать это IMHO - диаграмма:

enter image description here

Обратите внимание, что это очень упрощенное, специфичное для Windows представление.

Способ понять эту диаграмму состоит в том, что чем выше на диаграмме метод выделения памяти, тем выше используется реализация более высокого уровня . Но начнем снизу.

Диспетчер памяти в режиме ядра

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

VirtualAlloc / VirtualFree

Это API-интерфейсы самого низкого уровня , доступные в пользовательском режиме . Функция VirtualAlloc в основном вызывает ZwAllocateVirtualMemory , которая, в свою очередь, выполняет быстрый системный вызов до ring0, чтобы передать дальнейшую обработку менеджеру памяти ядра. Это также самый быстрый способ зарезервировать / выделить блок новой памяти из всех доступных в пользовательском режиме.

Но у него есть два основных условия:

  • Распределяет только блоки памяти, выровненные по границе гранулярности системы.

  • Распределяет блоки памяти только того размера, который кратен степени детализации системы.

Так что же это за гранулярность системы ? Вы можете получить его, позвонив GetSystemInfo . Возвращается как параметр dwAllocationGranularity. Его значение зависит от реализации (и, возможно, от аппаратного обеспечения), но во многих 64-разрядных системах Windows оно установлено на 0x10000 байт или 64K.

Так что все это означает, что если вы попытаетесь выделить, скажем, просто 8-байтовый блок памяти с VirtualAlloc:

void* pAddress = VirtualAlloc(NULL, 8, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

В случае успеха pAddress будет выровнено по границе байта 0x10000. И даже несмотря на то, что вы запросили только 8 байтов, фактический блок памяти, который вы получите, будет содержать все page (или что-то вроде 4K байтов. Точный размер страницы возвращается в dwPageSize параметр.) Но, кроме того, весь блок памяти, охватывающий 0x10000 байт (или 64K в большинстве случаев) из pAddress , не будет доступным для дальнейшего распределения. Таким образом, в некотором смысле, выделив 8 байт, вы могли бы также запросить 65536.

Таким образом, мораль этой истории заключается не в том, чтобы заменить VirtualAlloc общим распределением памяти в вашем приложении. Он должен использоваться для очень специфических случаев, как это делается с heap ниже. (Обычно для резервирования / выделения больших блоков памяти.)

Неправильное использование VirtualAlloc может привести к серьезной фрагментации памяти.

HeapCreate / HeapAlloc / HeapFree / HeapDestroy

В двух словах, функции heap в основном являются оболочкой для функции VirtualAlloc. Другие ответы здесь дают довольно хорошее представление об этом. Я добавлю, что в очень упрощенном виде способ heap работает так:

  • HeapCreate резервирует большой блок виртуальной памяти, вызывая VirtualAlloc для внутреннего использования (или ZwAllocateVirtualMemory для уточнения). Он также устанавливает внутреннюю структуру данных, которая может отслеживать дальнейшие распределения меньшего размера в зарезервированном блоке виртуальной памяти.

  • Любые вызовы HeapAlloc и HeapFree фактически не выделяют / освобождают какую-либо новую память (если, конечно, запрос не превышает то, что уже зарезервировано в HeapCreate), а вместо этого они метра * (или commit) ранее зарезервированного большого блока, разбивая его на меньшие блоки памяти, которые запрашивает пользователь.

  • HeapDestroy в свою очередь вызывает VirtualFree, что фактически освобождает виртуальную память.

Таким образом, все это делает функции heap идеальными кандидатами для общего распределения памяти в вашем приложении. Это отлично подходит для произвольного размера памяти. Но небольшая цена за удобство функций heap заключается в том, что они вносят небольшие издержки по сравнению с VirtualAlloc при резервировании больших блоков памяти.

Еще одна хорошая вещь в куче заключается в том, что вам не нужно создавать ее. Обычно он создается для вас, когда начинается ваш процесс. Таким образом, можно получить к нему доступ, вызвав функцию GetProcessHeap .

malloc / free

Является языковой оболочкой для функций heap . В отличие от HeapAlloc, HeapFree и т. Д. Эти функции будут работать не только в том случае, если ваш код скомпилирован для Windows, но и для других операционных систем (таких как Linux и т. Д.)

Это рекомендуемый способ выделения / освобождения памяти, если вы программируете на языке C. (Если только вы не кодируете определенный драйвер устройства режима ядра.)

новый / удалить

Приходите в качестве операторов высокого уровня (ну, для C++) операторов управления памятью. Они специфичны для языка C++ и, подобно malloc для C, также являются оболочками для функций heap. У них также есть целый набор собственного кода, который имеет дело с C++ -специфической инициализацией конструкторов, освобождением в деструкторах и т. Д.

Эти функции являются рекомендуемым способом выделения / освобождения памяти и объектов, если вы программируете на C++.


Наконец, я хочу сделать один комментарий о том, что было сказано в других ответах об использовании VirtualAlloc для разделения памяти между процессами. VirtualAlloc само по себе не позволяет делиться своей зарезервированной / выделенной памятью с другими процессами. Для этого необходимо использовать API CreateFileMapping, который может создавать именованный блок виртуальной памяти, который может использоваться другими процессами. Он также может отобразить файл на диске в виртуальную память для доступа на чтение / запись. Но это уже другая тема.

7 голосов
/ 16 мая 2009

В общих чертах:

  • VirtualAlloc, HeapAlloc и т. Д. - это API-интерфейсы Windows, которые выделяют память различных типов непосредственно из ОС. VirtualAlloc управляет страницами в системе виртуальной памяти Windows, а HeapAlloc выделяет из определенной кучи ОС. Честно говоря, вам вряд ли когда-нибудь понадобится использовать любой из них.

  • malloc - это стандартная библиотечная функция C (и C ++), которая выделяет память для вашего процесса. Реализации malloc обычно используют один из API-интерфейсов ОС для создания пула памяти при запуске приложения, а затем выделяют его при выполнении запросов malloc

  • new - это стандартный оператор C ++, который выделяет память и затем соответствующим образом вызывает конструкторы в этой памяти. Он может быть реализован в терминах malloc или в терминах API-интерфейсов ОС, и в этом случае он обычно создает пул памяти при запуске приложения.

4 голосов
/ 18 января 2010

VirtualAlloc ===> sbrk() под UNIX

HeapAlloc ====> malloc() под UNIX

2 голосов
/ 17 июня 2009

VirtualAlloc => Распределяет прямо в виртуальной памяти, вы резервируете / фиксируете в блоках. Это отлично подходит для больших выделений, например больших массивов.

HeapAlloc / new => выделяет память в куче по умолчанию (или любой другой куче, которую вы можете создать). Это распределяется на объект и отлично подходит для небольших объектов. Куча по умолчанию является сериализуемой, поэтому она имеет гарантированное распределение потоков (это может вызвать некоторые проблемы в высокопроизводительных сценариях, и поэтому вы можете создавать свои собственные кучи).

malloc => использует кучу времени выполнения C, аналогично HeapAlloc, но обычно для сценариев совместимости.

В двух словах, куча - это просто кусок виртуальной памяти, который управляется диспетчером кучи (а не сырой виртуальной памятью)

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

...