Существует множество способов выделения памяти в среде Windows, например VirtualAlloc
, HeapAlloc
, malloc
, new
.
Итак, какая между ними разница?
Существует множество способов выделения памяти в среде Windows, например VirtualAlloc
, HeapAlloc
, malloc
, new
.
Итак, какая между ними разница?
Каждый 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
, которые вам могут сказать, что вы должны использовать их в определенных обстоятельствах.
HeapAlloc
] для выделения памяти, которую вы им передаете, или использовали его компаньон HeapFree
для освобождения памяти, которую они вам возвращают. ‹‹‹ Есть примеры этого?
- person Assimilater; 27.07.2016
Очень важно понимать разницу между API-интерфейсами выделения памяти (в Windows), если вы планируете использовать язык, требующий управления памятью (например, C или C++). И лучший способ проиллюстрировать это ИМХО — это диаграмма:
Обратите внимание, что это очень упрощенное представление для Windows.
Чтобы понять эту диаграмму, нужно понять, что чем выше на диаграмме метод выделения памяти, тем более высокого уровня реализация, которую он использует. Но начнем снизу.
Он обеспечивает все резервирование и распределение памяти для операционной системы, а также поддерживает файлы с отображением памяти, общую память, копирование при записи операции и т. д. Он недоступен напрямую из кода пользовательского режима, поэтому я его здесь пропущу.
Это 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
может привести к серьезной фрагментации памяти.
В двух словах, функции heap в основном представляют собой оболочку для функции VirtualAlloc
. Другие ответы здесь дают довольно хорошее представление об этом. Я добавлю, что в очень упрощенном виде heap работает следующим образом:
HeapCreate
резервирует большой блок виртуальной памяти, вызывая VirtualAlloc
внутренне (или ZwAllocateVirtualMemory
, если быть точным). Он также устанавливает внутреннюю структуру данных, которая может отслеживать дальнейшие выделения меньшего размера в зарезервированном блоке виртуальной памяти.
Любые вызовы HeapAlloc
и HeapFree
на самом деле не выделяют/освобождают какую-либо новую память (если, конечно, запрос не превышает то, что уже зарезервировано в HeapCreate
), а вместо этого они измеряют (или commit
) ранее зарезервированную большой кусок, разбивая его на более мелкие блоки памяти, которые запрашивает пользователь.
HeapDestroy
, в свою очередь, вызывает VirtualFree
, который фактически освобождает виртуальную память.
Таким образом, все это делает функции heap идеальными кандидатами на общее выделение памяти в вашем приложении. Он отлично подходит для выделения памяти произвольного размера. Но небольшая цена за удобство функций heap заключается в том, что они создают небольшие накладные расходы по сравнению с VirtualAlloc
при резервировании больших блоков памяти.
Еще одно преимущество кучи заключается в том, что вам не нужно ее создавать. Обычно он создается для вас, когда ваш процесс начинается. Таким образом, к нему можно получить доступ, вызвав GetProcessHeap функция.
Является языковой оболочкой для функций heap. В отличие от HeapAlloc
, HeapFree
и т.д. эти функции будут работать не только если ваш код скомпилирован для Windows, но и для других операционных систем (таких как Linux и т.д.)
Это рекомендуемый способ выделения/освобождения памяти, если вы программируете на C. (Если только вы не пишете драйвер устройства в режиме ядра.)
Приходите как операторы высокого уровня (ну, для C++
) управления памятью. Они специфичны для языка C++
и, подобно malloc
для C
, также являются оболочками для функций heap
. У них также есть целая куча собственного кода, который обрабатывает специфичную для C++
инициализацию конструкторов, освобождение памяти в деструкторах, возбуждение исключения и т. д.
Эти функции являются рекомендуемым способом выделения/освобождения памяти и объектов, если вы программируете на C++
.
Наконец, один комментарий, который я хочу сделать по поводу того, что было сказано в других ответах об использовании VirtualAlloc
для совместного использования памяти между процессами. VirtualAlloc
сам по себе не позволяет совместно использовать свою зарезервированную/выделенную память с другими процессами. Для этого нужно использовать CreateFileMapping
< /a> API, который может создать именованный блок виртуальной памяти, который можно использовать совместно с другими процессами. Он также может отображать файл на диске в виртуальную память для чтения/записи. Но это другая тема.
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
, которое никто из ответивших на этот вопрос не обсуждал.
VirtualAlloc
Ex — это то, что один процесс может использовать для выделения памяти в адресном пространстве другого процесса. Чаще всего это используется в сочетании для удаленного выполнения в контексте другого процесса через CreateRemoteThread (аналогично CreateThread
, поток просто запускается в другом процессе).
В общих чертах:
VirtualAlloc, HeapAlloc и т. д. — это Windows API, которые выделяют память различных типов напрямую из ОС. VirtualAlloc управляет страницами в системе виртуальной памяти Windows, а HeapAlloc выделяет из определенной кучи ОС. Честно говоря, вам вряд ли когда-нибудь понадобится использовать любой из них.
malloc — это стандартная библиотечная функция C (и C++), которая выделяет память для вашего процесса. Реализации malloc обычно используют один из API-интерфейсов ОС для создания пула памяти при запуске вашего приложения, а затем выделяют из него по мере выполнения запросов malloc.
new — это стандартный оператор C++, который выделяет память, а затем соответствующим образом вызывает конструкторы для этой памяти. Он может быть реализован с помощью malloc или с точки зрения API-интерфейсов ОС, и в этом случае он также обычно создает пул памяти при запуске приложения.
VirtualAlloc
===> sbrk()
под UNIX
HeapAlloc
====> malloc()
под UNIX
VirtualAlloc
=> Выделяет прямо в виртуальную память, вы резервируете/фиксируете блоками. Это отлично подходит для больших распределений, например больших массивов.
HeapAlloc
/ new
=> выделяет память в куче по умолчанию (или любой другой куче, которую вы можете создать). Это распределяется по объектам и отлично подходит для небольших объектов. Куча по умолчанию является сериализуемой, поэтому она имеет гарантированное распределение потоков (это может вызвать некоторые проблемы в сценариях с высокой производительностью, поэтому вы можете создавать свои собственные кучи).
malloc
=> использует кучу среды выполнения C, аналогичную HeapAlloc
, но это обычно для сценариев совместимости.
В двух словах, куча — это просто кусок виртуальной памяти, которым управляет диспетчер кучи (а не необработанная виртуальная память).
Последняя модель в мире памяти — это файлы с отображением памяти, этот сценарий отлично подходит для больших блоков данных (например, больших файлов). Это используется внутри, когда вы открываете EXE (он не загружает EXE в память, просто создает файл с отображением памяти).