Jakie są różnice między VirtualAlloc i HeapAlloc?

82

Istnieje wiele metod, aby przydzielić pamięci w środowisku Windows, takie jak VirtualAlloc, HeapAlloc, malloc, new.

Jaka jest więc różnica między nimi?

Ajay
źródło

Odpowiedzi:

83

Każdy interfejs API jest przeznaczony do różnych zastosowań. Każdy z nich wymaga również użycia prawidłowej funkcji zwalniania / zwalniania, gdy skończysz z pamięcią.

VirtualAlloc

Niskopoziomowy interfejs API systemu Windows, który zapewnia wiele opcji, ale jest przydatny głównie dla osób w dość specyficznych sytuacjach. Można przydzielić pamięć tylko w (edytuj: nie 4KB) większych fragmentach. Są sytuacje, w których tego potrzebujesz, ale będziesz wiedział, że jesteś w jednej z takich sytuacji. Jednym z najczęstszych jest udostępnianie pamięci bezpośrednio innemu procesowi. Nie używaj go do alokacji pamięci ogólnego przeznaczenia. Służy VirtualFreedo zwalniania przydziału.

HeapAlloc

Alokuje pamięć o dowolnej wielkości, a nie w dużych porcjach VirtualAlloc. HeapAllocwie, kiedy musi zadzwonić, VirtualAlloci robi to automatycznie. Podobnie jak malloc, ale jest tylko dla systemu Windows i zapewnia kilka dodatkowych opcji. Nadaje się do przydzielania ogólnych fragmentów pamięci. Niektóre interfejsy API systemu Windows mogą wymagać użycia tej funkcji w celu przydzielenia przekazywanej im pamięci lub użycia jej elementu towarzyszącego w HeapFreecelu zwolnienia pamięci, którą zwracają.

malloc

Sposób przydzielania pamięci w języku C. Preferuj to, jeśli piszesz w C, a nie C ++ i chcesz, aby Twój kod działał np. Również na komputerach z systemem Unix lub ktoś konkretnie mówi, że musisz go używać. Nie inicjalizuje pamięci. Nadaje się do przydzielania ogólnych fragmentów pamięci, takich jak HeapAlloc. Prosty interfejs API. Służy freedo zwalniania przydziału. mallocWywołania Visual C ++ HeapAlloc.

Nowy

Sposób przydzielania pamięci w języku C ++. Wolisz to, jeśli piszesz w C ++. Umieszcza również obiekt lub obiekty w przydzielonej pamięci. Służy deletedo zwalniania alokacji (lub delete[]dla tablic). Visual Studio newwywołuje HeapAlloc, a następnie może inicjalizuje obiekty, w zależności od tego, jak to nazwiesz.

W najnowszych standardach C ++ (C ++ 11 i nowszych), jeśli musisz ręcznie używać delete, robisz to źle i zamiast tego powinieneś użyć inteligentnego wskaźnika, takiego jak unique_ptr. Począwszy od C ++ 14 można to samo powiedzieć new(zastąpione funkcjami takimi jak make_unique()).


Istnieje również kilka innych podobnych funkcji, takich jak SysAllocStringta, z której możesz zostać poinformowany, że musisz użyć w określonych okolicznościach.

Doug
źródło
18
Doug: VirtualAlloc nie jest ściśle ograniczony do alokacji 4kb, jest to rozmiar zwracany przez GetSystemInfo (), SYSTEM_INFO :: dwAllocationGranularity. Właściwie to RZADKO 4kb. Na moim hoście jest to 64k, podejrzewam, że dla ciebie to samo. 4KB to minimalna wielkość strony dla różnych tablic deskryptorów w ABI x86. 4KB to najmniejszy rozmiar, na który można zezwolić niezależnie, R / W / X, jednak nie ma to żadnego znaczenia dla VirtualAlloc. Jeśli odwołujesz się do dokumentacji VirtualAlloc, jest tam również opcja LARGE_PAGES (zobacz msdn.microsoft.com/en-us/library/aa366568(VS.85).aspx ).
RandomNickName42
Czy wiesz, dlaczego DirectShow używa VirtualAlloc do przydzielania buforów pamięci dla mediów zamiast używania malloc?
Aviad Rozenhek
Aviad: DirectShow używa wirtualnej alokacji do rezerwowania pamięci, aby mógł przekazywać flagi wymagane do optymalizacji wydajności, rzeczy takie jak brak możliwości stronicowania lub aby mógł zarezerwować fizyczne strony, które mogą poprawić wydajność, jeśli twój sprzęt może to obsługiwać.
RandomNickName42
8
@ RandomNickName42: to nie w porządku. Adres początkowy alokacji jest zawsze wyrównany z szczegółowością (64 KB), ale długość alokacji jest zaokrąglana do rozmiaru strony (4 KB). W efekcie przestrzeń adresowa jest zarezerwowana w fragmentach o rozmiarze 64 KB, ale zatwierdzana w fragmentach o rozmiarze 4KB. Wyrównanie adresu początkowego zwykle nie jest tak interesujące, więc z większości punktów widzenia VirtualAlloc działa w fragmentach 4KB. Te szczegóły stają się ważne tylko wtedy, gdy próbujesz zrobić dużo małych VirtualAlloc (zabraknie Ci przestrzeni adresowej) lub czegoś wyszukanego (np. Sąsiednie, ale oddzielne alokacje).
arx
Pomyślałem, że malloc / new / CRT o nazwie HeapAlloc kiedyś użył własnych algorytmów do zwrócenia bloków pamięci z bloku pamięci HeapAlloc?
paulm
31

Bardzo ważne jest, aby zrozumieć różnicę między interfejsami API alokacji pamięci (w systemie Windows), jeśli planujesz używać języka wymagającego zarządzania pamięcią (takiego jak C lub C ++). Najlepszym sposobem zilustrowania tego IMHO jest diagram:

wprowadź opis obrazu tutaj

Zauważ, że jest to bardzo uproszczony widok specyficzny dla systemu Windows.

Sposobem na zrozumienie tego diagramu jest to, że im wyżej na diagramie znajduje się metoda alokacji pamięci, tym wyższy poziom używa jej implementacji. Ale zacznijmy od dołu.

Menedżer pamięci trybu jądra

Zapewnia wszystkie rezerwacje i alokacji pamięci dla systemu operacyjnego, jak również wsparcie dla plików pamięci odwzorowany , współdzielonej pamięci , kopiowanie przy zapisie operacji, itd. To nie jest dostępny bezpośrednio z kodu trybu użytkownika, więc pominę to tutaj.

VirtualAlloc / VirtualFree

Są to interfejsy API najniższego poziomu dostępne w trybie użytkownika . VirtualAllocFunkcja zasadzie wywołuje ZwAllocateVirtualMemory który z kolei dokonuje szybkiej syscall się ring0do dalszego przetwarzania spycha do menedżera pamięci jądra. Jest to również najszybsza metoda rezerwacji / alokacji bloku nowej pamięci spośród wszystkich dostępnych w trybie użytkownika.

Ale ma dwa główne warunki:

  • Przydziela tylko bloki pamięci wyrównane na granicy szczegółowości systemu.

  • Przydziela tylko bloki pamięci o rozmiarze będącym wielokrotnością ziarnistości systemu.

Jaka jest więc szczegółowość systemu ? Możesz to uzyskać, wywołując GetSystemInfo . Jest zwracany jako dwAllocationGranularityparametr. Jego wartość jest specyficzna dla implementacji (i prawdopodobnie sprzętu), ale w wielu 64-bitowych systemach Windows jest ustawiana na 0x10000bajty lub 64K.

To wszystko oznacza, że ​​jeśli spróbujesz przydzielić, powiedz tylko 8-bajtowy blok pamięci z VirtualAlloc:

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

Jeśli się powiedzie, pAddresszostanie wyrównany na 0x10000granicy bajtów. I nawet jeśli zażądałeś tylko 8 bajtów, rzeczywisty blok pamięci, który otrzymasz, będzie całym page(lub czymś w rodzaju 4Kbajtów. Dokładny rozmiar strony jest zwracany w dwPageSizeparametrze). Ale poza tym cały blok pamięci zakres 0x10000bajtów (lub 64Kw większości przypadków) z pAddress nie będzie dostępny dla dalszych alokacji. W pewnym sensie, przydzielając 8 bajtów, równie dobrze możesz poprosić o 65536.

Zatem morał z tej historii nie polega na zastępowaniu VirtualAllocogólnych alokacji pamięci w aplikacji. Musi być używany w bardzo szczególnych przypadkach, tak jak jest to zrobione w przypadku stosu poniżej. (Zwykle do rezerwowania / przydzielania dużych bloków pamięci).

VirtualAllocNieprawidłowe użycie może prowadzić do poważnej fragmentacji pamięci.

HeapCreate / HeapAlloc / HeapFree / HeapDestroy

W skrócie, funkcje sterty są w zasadzie opakowaniem dla VirtualAllocfunkcji. Inne odpowiedzi tutaj stanowią całkiem niezłą koncepcję tego. Dodam, że w bardzo uproszczonym ujęciu sposób działania sterty jest następujący:

  • HeapCreaterezerwuje duży blok pamięci wirtualnej przez wywołanie VirtualAllocwewnętrzne (a ZwAllocateVirtualMemorykonkretnie). Tworzy również wewnętrzną strukturę danych, która może śledzić dalsze alokacje mniejszych rozmiarów w zarezerwowanym bloku pamięci wirtualnej.

  • Wszelkie wywołania HeapAlloci HeapFreefaktycznie nie przydzielają / zwalniają żadnej nowej pamięci (chyba że, oczywiście, żądanie przekracza to, co zostało już zarezerwowane HeapCreate), ale zamiast tego odliczają (lub commit) wcześniej zarezerwowany duży fragment, dzieląc go na mniejsze bloki pamięci, które żąda użytkownik.

  • HeapDestroyz kolei wywołania, VirtualFreektóre faktycznie zwalniają pamięć wirtualną.

Wszystko to sprawia, że funkcje sterty są idealnymi kandydatami do ogólnego przydziału pamięci w aplikacji. Świetnie nadaje się do alokacji pamięci o dowolnym rozmiarze. Jednak niewielką ceną za wygodę funkcji sterty jest to, że wprowadzają one niewielki narzut VirtualAllocprzy rezerwowaniu większych bloków pamięci.

Kolejną dobrą rzeczą w sterty jest to, że tak naprawdę nie musisz jej tworzyć. Zwykle jest tworzony dla Ciebie, gdy rozpoczyna się Twój proces. Można więc uzyskać do niego dostęp, wywołując funkcję GetProcessHeap .

malloc / free

Jest opakowaniem specyficznym dla języka dla funkcji sterty . W przeciwieństwie HeapAlloc, HeapFreeitd te funkcje nie będą działać tylko wtedy, gdy kod jest kompilowany dla systemu Windows, ale również dla innych systemów operacyjnych (takich jak Linux, itd)

Jest to zalecany sposób przydzielania / zwalniania pamięci, jeśli programujesz w języku C. (Chyba że kodujesz określony sterownik urządzenia trybu jądra).

nowy / usuń

Przyjdź jako operatorzy zarządzania pamięcią wysokiego poziomu (no cóż, dla C++). Są specyficzne dla C++języka i podobnie jak mallocdla C, są również opakowaniami dla heapfunkcji. Mają też całą masę własnego kodu, który zajmuje się C++specyficzną inicjalizacją konstruktorów, zwalnianiem alokacji w destruktorach, zgłaszaniem wyjątków itp.

Te funkcje są zalecanym sposobem przydzielania / zwalniania pamięci i obiektów, jeśli programujesz w C++.


Na koniec chciałbym poruszyć jeden komentarz dotyczący tego, co zostało powiedziane w innych odpowiedziach na temat VirtualAllocwspółdzielenia pamięci między procesami. VirtualAllocsamo w sobie nie pozwala na współdzielenie zarezerwowanej / alokowanej pamięci z innymi procesami. W tym celu należy użyć CreateFileMappinginterfejsu API, który może utworzyć nazwany blok pamięci wirtualnej, który można udostępniać innym procesom. Może również mapować plik na dysku do pamięci wirtualnej w celu uzyskania dostępu do odczytu / zapisu. Ale to inny temat.

ahmd0
źródło
29

VirtualAllocjest wyspecjalizowaną alokacją systemu pamięci wirtualnej (VM) systemu operacyjnego. Alokacje w systemie maszyny wirtualnej muszą być dokonywane na poziomie szczegółowości alokacji, która (szczegółowość alokacji) zależy od architektury. Alokacja w systemie VM jest jedną z najbardziej podstawowych form alokacji pamięci. Alokacje maszyn wirtualnych mogą przybierać różne formy, pamięć niekoniecznie musi być dedykowana lub fizycznie zabezpieczona w pamięci RAM (choć może tak być). Alokacja maszyn wirtualnych jest zwykle alokacją specjalnego przeznaczenia , ponieważ alokacja musi

  • być bardzo dużym,
  • musi być udostępniony,
  • muszą być dostosowane do określonej wartości (ze względu na wydajność) lub
  • dzwoniący nie musi używać całej tej pamięci naraz ...
  • itp...

HeapAllocjest w zasadzie to, co malloci newjak w końcu zadzwonić. Został zaprojektowany tak, aby był bardzo szybki i użyteczny w wielu różnych typach scenariuszy alokacji ogólnego przeznaczenia. Jest to „Sterta” w klasycznym tego słowa znaczeniu. Sterty są w rzeczywistości konfigurowane przez a VirtualAlloc, który jest używany do początkowej rezerwacji miejsca alokacji z systemu operacyjnego. Po zainicjowaniu przestrzeni przez VirtualAlloc, różne tabele, listy i inne struktury danych są konfigurowane w celu utrzymania i sterowania działaniem HEAP. Część tej operacji polega na dynamicznym skalowaniu (powiększaniu i zmniejszaniu) pryzmy, dostosowywaniu pryzmy do określonych zastosowań (częste alokacje pewnego rozmiaru) itp.

newi mallocsą nieco takie same, mallocjest zasadniczo dokładnym wezwaniem do HeapAlloc( heap-id-default ); newjednakże może [dodatkowo] skonfigurować przydzieloną pamięć dla obiektów C ++ . Dla danego obiektu C ++ będzie przechowywać vtables na stercie dla każdego wywołującego. Te tabele vtables są przekierowaniami do wykonania i stanowią część tego, co nadaje C ++ jego cechy OO, takie jak dziedziczenie, przeciążanie funkcji itp.

Niektóre inne popularne metody alokacji, takie jak _alloca()i _malloca()są oparte na stosie ; FileMappings są tak naprawdę przydzielane VirtualAlloci ustawiane za pomocą określonych flag bitowych, które określają te mapowania jako typu FILE.

W większości przypadków pamięć należy przydzielać w sposób zgodny z wykorzystaniem tej pamięci;). neww C ++, mallocdla C, VirtualAllocdla przypadków masowych lub IPC.

*** Uwaga, duże alokacje pamięci wykonywane przez HeapAllocsą w rzeczywistości wysyłane VirtualAllocpo pewnym rozmiarze (kilkaset k lub 16 MB lub coś, o czym zapomniałem, ale dość duże :)).

*** EDYCJA Krótko wspomniałem o IPC i VirtualAllocjest też coś bardzo fajnego w powiązaniu, o VirtualAllocktórym żaden z respondentów nie omówił.

VirtualAllocEx jest tym, czego jeden proces może użyć do przydzielenia pamięci w przestrzeni adresowej innego procesu. Najczęściej jest to używane w połączeniu, aby uzyskać zdalne wykonanie w kontekście innego procesu za pośrednictwem CreateRemoteThread (podobnie jak CreateThreadwątek jest po prostu uruchamiany w innym procesie).

RandomNickName42
źródło
7

W konturze:

  • VirtualAlloc, HeapAlloc itp. To interfejsy API systemu Windows, które przydzielają pamięć różnych typów bezpośrednio z systemu operacyjnego. VirtualAlloc zarządza stronami w systemie pamięci wirtualnej Windows, podczas gdy HeapAlloc przydziela z określonej sterty systemu operacyjnego. Szczerze mówiąc, prawdopodobnie nigdy nie będziesz musiał używać żadnego z nich.

  • malloc to standardowa funkcja biblioteki C (i C ++), która przydziela pamięć do twojego procesu. Implementacje malloc zazwyczaj używają jednego z interfejsów API systemu operacyjnego do tworzenia puli pamięci podczas uruchamiania aplikacji, a następnie alokowania z niej podczas wykonywania żądań malloc

  • new to standardowy operator C ++, który przydziela pamięć, a następnie odpowiednio wywołuje konstruktory w tej pamięci. Może być zaimplementowany w zakresie malloc lub w zakresie interfejsów API systemu operacyjnego, w którym to przypadku również zwykle tworzy pulę pamięci podczas uruchamiania aplikacji.


źródło
4

VirtualAlloc===> sbrk()pod UNIXem

HeapAlloc====> malloc()w systemie UNIX

Mrad Chems Eddine
źródło
To nie odpowiada na pytanie.
Dan Bechard
2

VirtualAlloc=> Alokuje bezpośrednio do pamięci wirtualnej, rezerwujesz / zatwierdzasz w blokach. Jest to świetne rozwiązanie w przypadku dużych alokacji, na przykład dużych tablic.

HeapAlloc/ new=> alokuje pamięć na domyślnym stosie (lub innym stosie, który możesz utworzyć). To przydziela na obiekt i jest świetne dla mniejszych obiektów. Domyślna sterta jest możliwa do serializacji, dlatego ma gwarantowaną alokację wątków (może to powodować pewne problemy w scenariuszach o wysokiej wydajności i dlatego można tworzyć własne sterty).

malloc=> używa sterty środowiska wykonawczego C, podobnie jak w HeapAllocprzypadku scenariuszy zgodności, ale jest to powszechne.

W skrócie, sterta to po prostu fragment pamięci wirtualnej zarządzanej przez menedżera sterty (zamiast surowej pamięci wirtualnej)

Ostatnim modelem w świecie pamięci są pliki mapowane w pamięci, ten scenariusz jest świetny dla dużych porcji danych (takich jak duże pliki). Jest to używane wewnętrznie podczas otwierania pliku EXE (nie ładuje pliku EXE do pamięci, tylko tworzy plik mapowany w pamięci).

Nie ma problemu
źródło