У чому різниця між VirtualAlloc та HeapAlloc?


82

Є багато методи виділення пам'яті в середовищі Windows, такі як VirtualAlloc, HeapAlloc, malloc, new.

Отже, яка різниця між ними?

Відповіді:


83

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

VirtualAlloc

Низькорівневий API Windows, який надає безліч опцій, але в основному корисний для людей у ​​досить конкретних ситуаціях. Може виділяти пам’ять лише у більших шматках (редагувати: не 4 КБ). Бувають ситуації, коли це вам потрібно, але ви будете знати, коли потрапите в одну з цих ситуацій. Одне з найпоширеніших - якщо вам доводиться ділитися пам’яттю безпосередньо з іншим процесом. Не використовуйте його для розподілу пам'яті загального призначення. Використовуйте VirtualFreeдля вивільнення.

HeapAlloc

Виділяє будь-який обсяг пам’яті, який ви просите, не великими шматками, ніж VirtualAlloc. HeapAllocзнає, коли йому потрібно зателефонувати, VirtualAllocі робить це для вас автоматично. Подобається malloc, але лише для Windows і надає ще кілька варіантів. Підходить для виділення загальних фрагментів пам'яті. Деякі API Windows можуть вимагати, щоб ви використовували це для виділення пам'яті, яку ви їм передаєте, або використовувати його супутник, HeapFreeщоб звільнити пам’ять, яку вони вам повертають.

малькок

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

новий

Спосіб розподілу пам'яті на C ++. Віддайте перевагу цьому, якщо ви пишете на C ++. Він також поміщає об'єкт або об'єкти у виділену пам'ять. Використовуйте deleteдля вивільнення (або delete[]для масивів). newВиклики Visual Studio HeapAlloc, а потім, можливо, ініціалізують об’єкти, залежно від того, як ви це називаєте.

В останні стандарти C ++ (C ++ 11 і вище), якщо ви повинні вручну використання delete, ви робите це неправильно , і повинні використовувати смарт - покажчик , як unique_ptrзамість цього. Починаючи з C ++ 14 і далі, те саме можна сказати про це new(замінено такими функціями, як make_unique()).


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


18
Дуг: 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 ).
RandomNickName42

Ви знаєте, чому DirectShow використовує VirtualAlloc для виділення буферів пам'яті для носіїв, а не для malloc?
Авіад Розенгек

Aviad: DirectShow використовує віртуальний alloc для резервування пам'яті, щоб він міг передавати прапори, необхідні для оптимізації продуктивності, такі речі, як недоступні для перегляду сторінок, або щоб він міг зарезервувати фізичні сторінки, які можуть покращити перформанс, якщо ваше обладнання може це підтримувати.
RandomNickName42

8
@ RandomNickName42: це неправильно. Початкова адреса розподілу завжди узгоджується з деталізацією (64 КБ), але довжина виділення округлюється до розміру сторінки (4 КБ). Фактично, адресний простір зарезервовано у фрагментах розміром 64 КБ, але зафіксовано у фрагментах 4 КБ. Вирівнювання початкової адреси зазвичай не таке цікаве, тому з більшості точок зору VirtualAlloc працює шматками в 4 КБ. Ці деталі стають важливими лише в тому випадку, якщо ви намагаєтеся зробити багато невеликих VirtualAllocs (у вас закінчиться адресний простір) або щось вигадливе (наприклад, сусідні, але окремі розподіли).
arx

Я думав, malloc / new / CRT під назвою HeapAlloc колись використовував власні алгоритми для повернення блоків пам'яті з блоку пам'яті HeapAlloc?
paulm

31

Дуже важливо зрозуміти різницю між API розподілу пам’яті (в Windows), якщо ви плануєте використовувати мову, яка вимагає управління пам’яттю (наприклад, C або C ++.) І найкращий спосіб проілюструвати це IMHO - це схема:

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

Зверніть увагу, що це дуже спрощений подання для 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Неправильне використання може призвести до сильної фрагментації пам'яті.

HeapCreate / HeapAlloc / HeapFree / HeapDestroy

У двох словах, функції купи в основному є обгорткою для VirtualAllocфункції. Інші відповіді тут дають досить хорошу концепцію цього. Додам, що, в дуже спрощеному поданні, купа працює так:

  • HeapCreateрезервує великий блок віртуальної пам'яті шляхом VirtualAllocвнутрішнього виклику (або, ZwAllocateVirtualMemoryякщо бути конкретним). Він також встановлює внутрішню структуру даних, яка може відслідковувати подальші розподіли менших розмірів у зарезервованому блоці віртуальної пам'яті.

  • Будь-які дзвінки HeapAllocта HeapFreeфактично не виділяють / звільняють нову пам’ять (якщо, звичайно, запит не перевищує того, що вже зарезервовано HeapCreate), але замість цього вони вимірюють (або commit) раніше зарезервований великий шматок, розтинаючи його на менші блоки пам'яті, які запити користувача.

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

Отже, все це робить функції купи ідеальними кандидатами для загального розподілу пам’яті у вашому додатку. Він чудово підходить для розміщення пам'яті довільного розміру. Але невелика ціна, яку потрібно заплатити за зручність функцій купи, полягає в тому, що вони створюють невеликі накладні витрати VirtualAllocпри резервуванні більших блоків пам'яті.

Ще одна хороша річ купи - те, що вам насправді не потрібно її створювати. Зазвичай він створюється для вас, коли починається процес. Тож можна отримати до нього доступ, зателефонувавши до функції GetProcessHeap .

malloc / безкоштовно

Це мовна оболонка для функцій купи . В відміну HeapAlloc, HeapFreeі т.д. ці функції будуть працювати не тільки тоді , коли ваш код скомпільовано для Windows, але і для інших операційних систем (наприклад, Linux і т.д.)

Це рекомендований спосіб розподілу / звільнення пам'яті, якщо ви програмуєте на C. (Якщо ви не кодуєте певний драйвер пристрою в режимі ядра).

новий / видалити

Станьте оператором управління пам’яттю високого рівня (ну, для C++). Вони специфічні для C++мови, і, як і mallocдля C, також є обгортками для heapфункцій. Вони також мають цілу купу власного коду, який займається - C++конкретною ініціалізацією конструкторів, вивільненням деструкторів, винятком тощо.

Ці функції є рекомендованим способом розподілу / звільнення пам'яті та об'єктів, якщо ви програмуєте C++.


Нарешті, я хочу зробити один коментар щодо того, що було сказано в інших відповідях щодо використання VirtualAllocдля спільного використання пам'яті між процесами. VirtualAllocсам по собі не дозволяє спільне використання своєї зарезервованої / виділеної пам'яті з іншими процесами. Для цього потрібно використовувати CreateFileMappingAPI, який може створити іменований блок віртуальної пам'яті, який можна спільно використовувати з іншими процесами. Він також може перетворити файл на диск на віртуальну пам'ять для доступу до читання / запису. Але це вже інша тема.


29

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

  • бути дуже великим,
  • потрібно ділитися,
  • повинні бути вирівняні за певним значенням (причини ефективності) або
  • абоненту не потрібно використовувати всю цю пам'ять одночасно ...
  • тощо ...

HeapAllocпо суті , що mallocі newяк в кінцевому підсумку викликати. Він розроблений для того, щоб бути дуже швидким та придатним для використання у багатьох різних типах сценаріїв загального призначення. Це "Купи" в класичному розумінні. Купи фактично налаштовуються за допомогою a VirtualAlloc, який використовується для початкового резервування місця виділення з ОС. Після ініціалізації простору VirtualAllocрізні таблиці, списки та інші структури даних налаштовуються на підтримку та контроль роботи HEAP. Частина цієї операції полягає у формі динамічного розміру (вирощування та зменшення) купи, пристосування купи до певного звичаю (часті виділення певного розміру) тощо.

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

Деякі інші методи загального розподілу , як _alloca()і _malloca()в стек основі; FileMappings дійсно виділяються VirtualAllocта встановлюються з певними бітовими прапорами, які позначають ці зіставлення як тип FILE.

Більшу частину часу слід розподіляти пам’ять так, щоб це відповідало використанню цієї пам’яті;). newв C ++, mallocдля C, VirtualAllocдля масових випадків або випадків IPC.

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

*** EDIT Я коротко зауважив про МПК, і VirtualAllocє також щось дуже акуратне у пов'язаному, VirtualAllocщо ніхто з респондентів на це питання не обговорював.

VirtualAllocEx - це те, що один процес може використовувати для розподілу пам'яті в адресному просторі іншого процесу. Найчастіше це використовується в поєднанні для отримання віддаленого виконання в контексті іншого процесу за допомогою CreateRemoteThread (подібно до того CreateThread, що потік просто запускається в іншому процесі).


7

У контурі:

  • VirtualAlloc, HeapAlloc тощо - це API Windows, які безпосередньо виділяють пам'ять різних типів з ОС. VirtualAlloc керує сторінками у системі віртуальної пам'яті Windows, тоді як HeapAlloc виділяє з певної купи ОС. Чесно кажучи, навряд чи вам коли-небудь потрібно буде використовувати будь-який із них.

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

  • new - це стандартний оператор C ++, який виділяє пам'ять, а потім викликає конструктори відповідно до цієї пам'яті. Він може бути реалізований з точки зору malloc або з точки зору API API, і в цьому випадку він також зазвичай створює пул пам'яті під час запуску програми.



2

VirtualAlloc=> Виділяє прямо у віртуальну пам’ять, ви резервуєте / фіксуєте блоками. Це чудово для великих розподілів, наприклад великих масивів.

HeapAlloc / new => виділяє пам'ять у купі за замовчуванням (або будь-якій іншій купі, яку ви можете створити). Це виділяє на об’єкт і чудово підходить для менших об’єктів. Купи за замовчуванням можна серіалізувати, тому він має гарантований розподіл потоків (це може спричинити деякі проблеми у сценаріях високої продуктивності, і тому ви можете створити власні купи).

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

У двох словах, купа - це лише шматок віртуальної пам'яті, якою керує диспетчер купи (а не необроблена віртуальна пам'ять)

Остання модель у світі пам'яті - це файли, зіставлені з пам'яттю, цей сценарій чудово підходить для великої кількості даних (наприклад, великих файлів). Це використовується внутрішньо, коли ви відкриваєте EXE (він не завантажує EXE в пам'ять, а лише створює файл, зіставлений із пам'яттю).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.