В чем разница между VirtualAlloc и HeapAlloc?

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

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


person Community    schedule 16.05.2009    source источник


Ответы (6)


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

Виртуаллок

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

кучааллок

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

маллок

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

новый

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

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


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

person Doug    schedule 16.05.2009
comment
Дуг: VirtualAlloc строго не ограничивается выделением 4 КБ, это размер, возвращаемый GetSystemInfo(), SYSTEM_INFO::dwAllocationGranularity. На самом деле это РЕДКО 4 КБ. У меня на хосте 64к, подозреваю у вас тоже самое. 4 КБ — это минимальный размер страницы для различных таблиц дескрипторов в x86 ABI. 4 КБ — это наименьший размер, который может иметь независимое разрешение, R/W/X, однако это не имеет никакого значения для VirtualAlloc. Если вы обратитесь к документации VirtualAlloc, там также есть параметр LARGE_PAGES (см. msdn.microsoft.com/en-us/library/aa366568(VS.85).aspx). - person RandomNickName42; 16.05.2009
comment
Знаете ли вы, почему DirectShow использует VirtualAlloc для выделения буферов памяти для медиа вместо использования malloc? - person Aviad Rozenhek; 12.05.2011
comment
Aviad: DirectShow использует виртуальное выделение памяти для резервирования памяти, поэтому он может передавать флаги, необходимые для оптимизации производительности, такие вещи, как невозможность страниц, или чтобы он мог резервировать физические страницы, которые могут улучшить производительность, если ваше оборудование может это поддерживать. - person RandomNickName42; 14.05.2011
comment
@RandomNickName42: это неправильно. Начальный адрес выделения всегда соответствует степени детализации (64 КБ), но длина выделения округляется до размера страницы (4 КБ). По сути, адресное пространство резервируется блоками по 64 КБ, но фиксируется блоками по 4 КБ. Выравнивание начального адреса обычно не так интересно, поэтому с большинства точек зрения VirtualAlloc работает фрагментами по 4 КБ. Эти детали становятся важными только в том случае, если вы пытаетесь выполнить множество небольших операций VirtualAlloc (у вас закончится адресное пространство) или что-то необычное (например, смежные, но отдельные выделения). - person arx; 14.03.2013
comment
Я думал, что malloc/new/CRT однажды вызвал HeapAlloc, а затем использовал свои собственные алгоритмы для возврата блоков памяти из блока памяти HeapAlloc? - person paulm; 05.07.2013
comment
2arx: Я хотел бы написать что-то подобное. По сути, адресное пространство резервируется блоками по 64 КБ, но фиксируется блоками по 4 КБ. , да это сильное ограничение. - person bruziuz; 29.01.2015
comment
Не используйте его для выделения памяти общего назначения. Смешное заявление, не имеющее под собой никаких оснований. - person Java Man Tea Man; 27.02.2016
comment
Некоторые API Windows могут потребовать, чтобы вы использовали [HeapAlloc] для выделения памяти, которую вы им передаете, или использовали его компаньон HeapFree для освобождения памяти, которую они вам возвращают. ‹‹‹ Есть примеры этого? - person Assimilater; 27.07.2016

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

введите описание изображения здесь

Обратите внимание, что это очень упрощенное представление для 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 общим распределением памяти в вашем приложении. Его нужно использовать в очень специфических случаях, как это сделано с кучей ниже. (Обычно для резервирования/выделения больших блоков памяти.)

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

Создание кучи / HeapAlloc / HeapFree / HeapDestroy

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

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

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

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

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

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

malloc / бесплатно

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

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

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

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

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


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

person ahmd0    schedule 18.05.2017

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

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

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

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

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

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

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

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

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

person RandomNickName42    schedule 16.05.2009

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

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

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

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

person Community    schedule 16.05.2009

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

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

person Mrad Chems Eddine    schedule 18.01.2010
comment
Это не отвечает на вопрос. - person Dan Bechard; 16.05.2017

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

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

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

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

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

person No hay Problema    schedule 17.06.2009