Каковы различия между VirtualAlloc и HeapAlloc?

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

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

Ответ 1

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

VirtualAlloc

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

HeapAlloc

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

таНос

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

новый

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

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


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

Ответ 2

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

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

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

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

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

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

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

*** ИЗМЕНИТЬ Я кратко заметил о IPC и VirtualAlloc, есть также что-то очень аккуратное относительно связанного VirtualAlloc, о котором ни один из респондентов этого вопроса не обсуждал.

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

Ответ 3

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

enter image description here

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

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

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

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

VirtualAlloc/VirtualFree

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

Но это идет с двумя основными условиями:

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

  • Он выделяет только блоки памяти, размер которых кратен степени детализации системы.

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

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

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

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

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

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

HeapCreate/HeapAlloc/HeapFree/HeapDestroy

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

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

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

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

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

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

malloc/free

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

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

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

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

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


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

Ответ 4

В схеме:

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

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

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

Ответ 5

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

HeapAlloc ==== > malloc() в UNIX

Ответ 6

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

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

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

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

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