Was sind die Unterschiede zwischen VirtualAlloc und HeapAlloc?

81

Es gibt viele Verfahren , um Speicher in Windows - Umgebung zuweisen, wie VirtualAlloc, HeapAlloc, malloc, new.

Was ist also der Unterschied zwischen ihnen?

Ajay
quelle

Antworten:

82

Jede API ist für unterschiedliche Zwecke bestimmt. Jeder erfordert auch, dass Sie die richtige Freigabe- / Freigabefunktion verwenden, wenn Sie mit dem Speicher fertig sind.

VirtualAlloc

Eine Windows-API auf niedriger Ebene, die viele Optionen bietet, aber hauptsächlich für Personen in bestimmten Situationen nützlich ist. Kann nur Speicher in größeren Blöcken zuweisen (bearbeiten: nicht 4 KB). Es gibt Situationen, in denen Sie es brauchen, aber Sie werden wissen, wann Sie sich in einer dieser Situationen befinden. Eine der häufigsten ist, wenn Sie den Speicher direkt mit einem anderen Prozess teilen müssen. Verwenden Sie es nicht für die allgemeine Speicherzuweisung. Verwenden Sie VirtualFreeausplanen.

HeapAlloc

Weist die gewünschte Speichergröße zu, nicht in großen Blöcken als VirtualAlloc. HeapAllocweiß, wann es anrufen muss VirtualAllocund erledigt dies automatisch für Sie. Wie malloc, ist aber nur Windows und bietet ein paar weitere Optionen. Geeignet zum Zuweisen allgemeiner Speicherblöcke. Einige Windows-APIs erfordern möglicherweise, dass Sie dies verwenden, um Speicher zuzuweisen, den Sie an sie übergeben, oder den Begleiter verwenden HeapFree, um Speicher freizugeben, den sie an Sie zurückgeben.

malloc

Die C-Methode zum Zuweisen von Speicher. Ziehen Sie dies vor, wenn Sie in C anstatt in C ++ schreiben und möchten, dass Ihr Code auch auf Unix-Computern funktioniert, oder wenn jemand ausdrücklich sagt, dass Sie ihn verwenden müssen. Initialisiert den Speicher nicht. Geeignet für die Zuweisung allgemeiner Speicherblöcke, wie z HeapAlloc. Eine einfache API. Verwenden Sie freeausplanen. Visual C ++ - mallocAufrufe HeapAlloc.

Neu

Die C ++ - Methode zum Zuweisen von Speicher. Ziehen Sie dies vor, wenn Sie in C ++ schreiben. Es legt auch ein Objekt oder Objekte in den zugewiesenen Speicher. Verwenden Sie deletediese Option, um die Zuordnung aufzuheben (oder delete[]für Arrays). Visual Studio newruft HeapAllocdie Objekte auf und initialisiert sie dann möglicherweise, je nachdem, wie Sie sie aufrufen.

Wenn Sie in neueren C ++ - Standards (C ++ 11 und höher) manuell verwenden müssen delete, machen Sie es falsch und sollten stattdessen einen intelligenten Zeiger wie unique_ptrverwenden. Ab C ++ 14 gilt das Gleiche new(ersetzt durch Funktionen wie make_unique()).


Es gibt auch einige andere ähnliche Funktionen SysAllocString, von denen Ihnen möglicherweise mitgeteilt wird, dass Sie sie unter bestimmten Umständen verwenden müssen.

Doug
quelle
18
Doug: VirtualAlloc ist nicht streng auf 4-KB-Zuweisungen beschränkt, sondern die von GetSystemInfo (), SYSTEM_INFO :: dwAllocationGranularity, zurückgegebene Größe. Es ist eigentlich selten 4kb. Auf meinem Host sind es 64.000, ich vermute dasselbe für Sie. 4 KB ist der Mindesteintrag für die Seitengröße für die verschiedenen Diskriptortabellen im x86-ABI. 4 KB ist die kleinste Größe, die unabhängig genehmigt werden kann, R / W / X, sie ist jedoch für VirtualAlloc nicht von Bedeutung. Wenn Sie auf die VirtualAlloc-Dokumentation verweisen, gibt es auch die Option LARGE_PAGES (siehe msdn.microsoft.com/en-us/library/aa366568(VS.85).aspx ).
RandomNickName42
Wissen Sie, warum DirectShow VirtualAlloc verwendet, um Speicherpuffer für Medien zuzuweisen, anstatt malloc zu verwenden?
Aviad Rozenhek
Aviad: DirectShow verwendet die virtuelle Zuweisung, um Speicher zu reservieren, damit Flags übergeben werden können, die für Leistungsoptimierungen erforderlich sind, z. B. nicht pageable, oder um physische Seiten zu reservieren, die die Leistung verbessern können, wenn Ihre Hardware dies unterstützt.
RandomNickName42
8
@ RandomNickName42: das ist nicht richtig. Die Zuordnungsstartadresse ist immer an der Granularität (64 KB) ausgerichtet, die Zuordnungslänge wird jedoch auf die Seitengröße (4 KB) gerundet. Tatsächlich ist der Adressraum in 64-KB-Blöcken reserviert, in 4-KB-Blöcken jedoch festgeschrieben. Die Ausrichtung der Startadresse ist normalerweise nicht so interessant, sodass VirtualAlloc aus den meisten Blickwinkeln in 4-KB-Blöcken funktioniert. Diese Details werden nur wichtig, wenn Sie versuchen, viele kleine VirtualAllocs (Ihnen geht der Adressraum aus) oder etwas Besonderes (wie benachbarte, aber separate Zuordnungen) auszuführen.
Arx
Ich dachte, malloc / new / CRT namens HeapAlloc hat einmal seine eigenen Algorithmen verwendet, um Speicherblöcke aus dem HeapAlloc-Speicherblock zurückzugeben.
Paulm
29

VirtualAllocist eine spezielle Zuordnung des VM-Systems (Virtual Memory) des Betriebssystems. Zuweisungen im VM-System müssen mit einer Zuordnungsgranularität erfolgen, die (die Zuordnungsgranularität) von der Architektur abhängt. Die Zuweisung im VM-System ist eine der grundlegendsten Formen der Speicherzuweisung. VM-Zuweisungen können verschiedene Formen annehmen. Der Speicher ist nicht unbedingt dediziert oder wird physisch im RAM gesichert (obwohl dies möglich ist). Die VM-Zuweisung ist in der Regel eine spezielle Zuweisung, entweder aufgrund der Zuweisung

  • sehr groß sein,
  • muss geteilt werden,
  • muss auf einen bestimmten Wert ausgerichtet sein (Leistungsgründe) oder
  • Der Anrufer muss nicht den gesamten Speicher auf einmal verwenden ...
  • etc...

HeapAllocist im Wesentlichen was mallocund newbeide nennen schließlich. Es ist so konzipiert, dass es unter vielen verschiedenen Szenarien einer Allzweckzuweisung sehr schnell und verwendbar ist. Es ist der "Haufen" im klassischen Sinne. Heaps werden tatsächlich von a eingerichtet VirtualAlloc. Dies wird verwendet, um zunächst Speicherplatz vom Betriebssystem zu reservieren. Nachdem der Speicherplatz durch initialisiert wurde VirtualAlloc, werden verschiedene Tabellen, Listen und andere Datenstrukturen konfiguriert, um den Betrieb des HEAP aufrechtzuerhalten und zu steuern. Ein Teil dieser Operation besteht in der dynamischen Dimensionierung (Vergrößerung und Verkleinerung) des Heaps, der Anpassung des Heaps an bestimmte Verwendungszwecke (häufige Zuweisungen einiger Größen) usw.

newund mallocsind etwas gleich, mallocist im wesentlichen ein exakter Aufruf in HeapAlloc( heap-id-default ); newkann jedoch [zusätzlich] den zugewiesenen Speicher für C ++ - Objekte konfigurieren . Für ein bestimmtes Objekt speichert C ++ für jeden Aufrufer vtables auf dem Heap. Diese vtables sind Weiterleitungen für die Ausführung und Teil dessen, was C ++ seine OO-Eigenschaften wie Vererbung, Funktionsüberladung usw. verleiht.

Einige andere gängige Zuweisungsmethoden wie _alloca()und _malloca()sind stapelbasiert . FileMappings werden wirklich VirtualAllocbestimmten Bit-Flags zugewiesen und mit diesen gesetzt, die diese Zuordnungen als vom Typ kennzeichnen FILE.

Meistens sollten Sie den Speicher so zuweisen, dass er mit der Verwendung dieses Speichers übereinstimmt;). newin C ++ mallocfür C VirtualAllocfür massive oder IPC-Fälle.

*** Beachten Sie, dass große Speicherzuweisungen, die von vorgenommen HeapAllocwerden, tatsächlich VirtualAllocnach einer gewissen Größe ausgeliefert werden (ein paar hundert k oder 16 MB oder etwas, das ich vergessen habe, aber ziemlich groß :)).

*** BEARBEITEN Ich habe kurz auf IPC hingewiesen und VirtualAlloces gibt auch etwas sehr Ordentliches an einem verwandten, VirtualAllocdas keiner der Antwortenden auf diese Frage besprochen hat.

VirtualAllocEx ist das, was ein Prozess verwenden kann, um Speicher in einem Adressraum eines anderen Prozesses zuzuweisen . In der Regel wird dies in Kombination verwendet , um die Remote-Ausführung im Kontext eines anderen Prozesses über CreateRemoteThread abzurufen (ähnlich wie CreateThreadder Thread nur im anderen Prozess ausgeführt wird).

RandomNickName42
quelle
28

Es ist sehr wichtig, die Unterscheidung zwischen Speicherzuweisungs-APIs (in Windows) zu verstehen, wenn Sie eine Sprache verwenden möchten, die eine Speicherverwaltung erfordert (wie C oder C ++). IMHO lässt sich dies am besten anhand eines Diagramms veranschaulichen:

Geben Sie hier die Bildbeschreibung ein

Beachten Sie, dass dies eine sehr vereinfachte, Windows-spezifische Ansicht ist.

Um dieses Diagramm zu verstehen, gilt: Je höher eine Speicherzuweisungsmethode im Diagramm ist, desto höher ist die Implementierung, die sie verwendet. Aber fangen wir von unten an.

Kernel-Modus-Speichermanager

Es bietet alle Speicherreservierungen und -zuordnungen für das Betriebssystem sowie Unterstützung für Dateien mit Speicherzuordnung , gemeinsam genutzten Speicher , Kopier- und Schreibvorgänge usw. Es ist nicht direkt über den Benutzermodus-Code zugänglich, daher überspringe ich hier.

VirtualAlloc / Virtual

Dies sind die APIs der niedrigsten Ebene, die im Benutzermodus verfügbar sind . Die VirtualAllocFunktion ruft im Grunde ZwAllocateVirtualMemory das wiederum macht eine schnelle syscall zu ring0weiterer Verarbeitung an den Kernel - Speicher - Manager zu verbannen. Dies ist auch die schnellste Methode, um einen Block neuen Speichers von allen im Benutzermodus verfügbaren zu reservieren / zuzuweisen.

Aber es kommt mit zwei Hauptbedingungen:

  • Es werden nur Speicherblöcke zugewiesen, die an der Systemgranularitätsgrenze ausgerichtet sind.

  • Es werden nur Speicherblöcke mit der Größe zugewiesen, die das Vielfache der Systemgranularität ist.

Was ist diese Systemgranularität ? Sie können es erhalten, indem Sie GetSystemInfo aufrufen . Es wird als dwAllocationGranularityParameter zurückgegeben. Sein Wert ist implementierungs- (und möglicherweise hardwarespezifisch), aber auf vielen 64-Bit-Windows-Systemen ist er auf 0x10000Bytes oder festgelegt 64K.

Das alles bedeutet also, dass Sie beim Zuweisen nur einen 8-Byte-Speicherblock sagen mit VirtualAlloc:

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

Wenn erfolgreich, pAddresswird an der 0x10000Bytegrenze ausgerichtet . Und obwohl Sie nur 8 Bytes angefordert haben, ist der tatsächliche Speicherblock, den Sie erhalten, der gesamte page(oder etwa 4KBytes. Die genaue Seitengröße wird im dwPageSizeParameter zurückgegeben.) Darüber hinaus der gesamte Speicherblock Spanning 0x10000Bytes (oder 64Kin den meisten Fällen) von stehen für weitere Zuordnungen pAddress nicht zur Verfügung. In gewisser Weise könnten Sie durch Zuweisen von 8 Bytes auch nach 65536 fragen.

Die Moral der Geschichte hier besteht also nicht darin, VirtualAllocgenerische Speicherzuordnungen in Ihrer Anwendung zu ersetzen . Es muss für ganz bestimmte Fälle verwendet werden, wie dies mit dem folgenden Heap geschieht . (Normalerweise zum Reservieren / Zuweisen großer Speicherblöcke.)

Eine VirtualAllocfalsche Verwendung kann zu einer starken Speicherfragmentierung führen.

HeapCreate / HeapAlloc / HeapFree / HeapDestroy

Kurz gesagt, die Heap- Funktionen sind im Grunde genommen ein Wrapper für VirtualAllocFunktionen. Andere Antworten hier liefern ein ziemlich gutes Konzept dafür. Ich werde hinzufügen, dass in einer sehr vereinfachten Ansicht die Art und Weise, wie Heap funktioniert, folgende ist:

  • HeapCreatereserviert einen großen Block virtuellen Speichers durch VirtualAllocinternen Aufruf (oder ZwAllocateVirtualMemoryum genau zu sein). Außerdem wird eine interne Datenstruktur eingerichtet, mit der weitere kleinere Zuordnungen innerhalb des reservierten Blocks des virtuellen Speichers verfolgt werden können.

  • Anrufe zu HeapAllocund HeapFreenicht wirklich zuordnen / frei , alle neuen Speicher (es sei denn natürlich die Anforderung hinaus , was bereits in reserviert ist HeapCreate) , sondern sie Meter aus (oder commit) ein zuvor reservierten großen Teil, indem sie sie in kleinere Speicherblöcke zu sezieren , dass ein Benutzer fordert.

  • HeapDestroyAufrufe VirtualFree, die tatsächlich den virtuellen Speicher freigeben.

All dies macht Heap- Funktionen zu perfekten Kandidaten für generische Speicherzuordnungen in Ihrer Anwendung. Es eignet sich hervorragend für Speicherzuordnungen beliebiger Größe. Ein kleiner Preis für die Bequemlichkeit der Heap- Funktionen besteht jedoch darin, dass sie einen geringen Overhead verursachen, VirtualAllocwenn größere Speicherblöcke reserviert werden.

Eine weitere gute Sache bei Heap ist, dass Sie nicht wirklich einen erstellen müssen. Es wird in der Regel für Sie erstellt, wenn Ihr Prozess beginnt. Sie können also darauf zugreifen, indem Sie die Funktion GetProcessHeap aufrufen .

malloc / frei

Ist ein sprachspezifischer Wrapper für die Heap- Funktionen. Im Gegensatz zu HeapAlloc, HeapFreewerden usw. Diese Funktionen arbeiten nicht nur , wenn Ihr Code für Windows kompiliert wird, sondern auch für andere Betriebssysteme (wie Linux, etc.)

Dies ist eine empfohlene Methode zum Zuweisen / Freigeben von Speicher, wenn Sie in C programmieren. (Es sei denn, Sie codieren einen bestimmten Gerätetreiber im Kernelmodus.)

neu / löschen

Kommen Sie als hochrangige (gut für C++) Speicherverwaltungsoperatoren. Sie sind spezifisch für die C++Sprache und wie mallocfür Csind auch die Wrapper für die heapFunktionen. Sie haben auch eine ganze Reihe ihres eigenen Codes, der sich mit der C++spezifischen Initialisierung von Konstruktoren, der Freigabe von Destruktoren, dem Auslösen einer Ausnahme usw. befasst.

Diese Funktionen sind eine empfohlene Methode zum Zuweisen / Freigeben von Speicher und Objekten, wenn Sie programmieren C++.


Zuletzt möchte ich einen Kommentar zu dem abgeben, was in anderen Antworten über die Verwendung VirtualAlloczum Teilen des Speichers zwischen Prozessen gesagt wurde . VirtualAllocan sich erlaubt es nicht, seinen reservierten / zugewiesenen Speicher mit anderen Prozessen zu teilen. Dazu muss eine CreateFileMappingAPI verwendet werden, die einen benannten virtuellen Speicherblock erstellen kann, der für andere Prozesse freigegeben werden kann. Es kann auch eine Datei auf der Festplatte für den Lese- / Schreibzugriff dem virtuellen Speicher zuordnen. Aber das ist ein anderes Thema.

ahmd0
quelle
7

In Umrissen:

  • VirtualAlloc, HeapAlloc usw. sind Windows-APIs, die Speicher verschiedener Typen direkt vom Betriebssystem zuweisen. VirtualAlloc verwaltet Seiten im virtuellen Windows-Speichersystem, während HeapAlloc von einem bestimmten Betriebssystem-Heap aus zuweist. Ehrlich gesagt ist es unwahrscheinlich, dass Sie jemals einen von ihnen verwenden müssen.

  • malloc ist eine Standard-C- (und C ++) Bibliotheksfunktion, die Ihrem Prozess Speicher zuweist. Bei Implementierungen von malloc wird normalerweise eine der Betriebssystem-APIs verwendet, um beim Start Ihrer App einen Speicherpool zu erstellen und diesen dann zuzuweisen, wenn Sie malloc-Anforderungen stellen

  • new ist ein Standard-C ++ - Operator, der Speicher zuweist und dann Konstruktoren in diesem Speicher entsprechend aufruft. Es kann in Form von Malloc oder in Bezug auf die Betriebssystem-APIs implementiert werden. In diesem Fall wird beim Start der Anwendung normalerweise auch ein Speicherpool erstellt.


quelle
4

VirtualAlloc===> sbrk()unter UNIX

HeapAlloc====> malloc()unter UNIX

Mrad Chems Eddine
quelle
Dies beantwortet die Frage nicht.
Dan Bechard
2

VirtualAlloc=> Wird direkt in den virtuellen Speicher zugewiesen, reservieren / festschreiben Sie in Blöcken. Dies ist ideal für große Zuordnungen, z. B. große Arrays.

HeapAlloc/ new=> ordnet den Speicher auf dem Standardheap (oder einem anderen von Ihnen erstellten Heap) zu. Dies wird pro Objekt zugewiesen und eignet sich hervorragend für kleinere Objekte. Der Standard-Heap ist serialisierbar und verfügt daher über eine garantierte Thread-Zuweisung (dies kann in Hochleistungsszenarien zu Problemen führen, weshalb Sie Ihre eigenen Heaps erstellen können).

malloc=> verwendet den C-Laufzeitheap, ähnlich wie HeapAllocbei Kompatibilitätsszenarien.

Kurz gesagt, der Heap ist nur ein Teil des virtuellen Speichers, der von einem Heap-Manager gesteuert wird (und nicht von einem virtuellen Rohspeicher).

Das letzte Modell in der Speicherwelt sind Dateien mit Speicherzuordnung. Dieses Szenario eignet sich hervorragend für große Datenmengen (wie große Dateien). Dies wird intern verwendet, wenn Sie eine EXE-Datei öffnen (die EXE-Datei wird nicht in den Speicher geladen, sondern nur eine Speicherzuordnungsdatei erstellt).

Kein Heu Problema
quelle