Systemspeicherzuordnung unter Verwendung des malloc-Subsystems

Der Speicher wird Anwendungen über das Subsystem malloc zugeordnet.

Das Subsystem malloc ist eine Speicherverwaltungs-API, die aus den folgenden Subroutinen besteht:

  • malloc
  • calloc
  • realloc
  • free
  • mallopt
  • mallinfo
  • alloca
  • valloc
  • posix_memalign

Das Subsystem malloc verwaltet ein logisches Speicherobjekt, das als Heapspeicherbezeichnet wird. Der Heapspeicher ist eine Speicherregion, die sich im Adressraum der Anwendung zwischen dem letzten Datenbyte, das vom Compiler zugeordnet wurde, und dem Ende der Datenregion befindet. Der Heapspeicher ist das Speicherobjekt, von dem Speicher zugeordnet wird und dem Speicher von der malloc -Subsystem-API zurückgegeben wird.

Das Subsystem malloc führt die folgenden grundlegenden Speicheroperationen aus:
  • Zuordnung:

    Ausgeführt von den Subroutinen malloc, calloc valloc, allocaund posix_memalign .

  • Freigabe:

    Wird von der Subroutine free ausgeführt

  • Neuzuteilung:

    Wird von der Subroutine realloc ausgeführt

Die Subroutinen mallopt und mallinfo werden aus Gründen der Kompatibilität mit System V unterstützt. Die Subroutine mallinfo kann während der Programmentwicklung verwendet werden, um Informationen zum Heapspeicher abzurufen, der von der Subroutine malloc verwaltet wird. Die Subroutine mallopt kann verwendet werden, um seitenausgerichteten freien Speicher in Seitengröße auszuschalten und die Standardzuordnungsfunktion zu inaktivieren. Ähnlich wie die Subroutine malloc wird die Subroutine valloc aus Gründen der Kompatibilität mit der Berkeley Compatibility Library bereitgestellt.

Weitere Informationen finden Sie in den folgenden Abschnitten:

Mit dem Prozesszwischenspeicher arbeiten

_edata ist ein Symbol, dessen Adresse das erste Byte nach dem letzten Byte der initialisierten Programmdaten ist. Das Symbol _edata bezieht sich auf den Start des Prozesszwischenspeichers, der durch das Subsystem malloc vergrößert wird, wenn der erste Datenblock zugeordnet wird. Das Subsystem malloc vergrößert den Prozessheapspeicher, indem es den Prozess-BRK-Wert erhöht, der das Ende des Prozessheapspeichers angibt. Dazu wird die Subroutine sbrk aufgerufen. Das Subsystem malloc erweitert den Prozessheapspeicher entsprechend den Anforderungen der Anwendung.

Der Prozesszwischenspeicher wird in zugeordnete und freigegebene Speicherblöcke unterteilt. Der freie Pool besteht aus dem für die nachfolgende Zuordnung verfügbaren Speicher. Eine Zuordnung wird abgeschlossen, indem zunächst ein Speicherblock aus dem freien Pool entfernt und dann ein Zeiger auf diesen Block zur aufrufenden Funktion zurückgegeben wird. Eine Neuzuordnung wird abgeschlossen, indem ein Speicherblock der neuen Größe zugeordnet wird, die Daten im ursprünglichen Block in den neuen Block verschoben werden und der ursprüngliche Block freigegeben wird. Die zugeordneten Speicherblöcke bestehen aus den Teilen des von der Anwendung verwendeten Prozesszwischenspeichers. Da die Speicherblöcke nicht physisch aus dem Heapspeicher entfernt werden (sie ändern den Status von 'frei' in 'zugeordnet'), nimmt die Größe des Prozessheapspeichers nicht ab, wenn der Speicher von der Anwendung freigegeben wird.

Prozessadressraum in 32-Bit-Anwendungen

Ein 32-Bit-Anwendungsprogramm, das auf dem System ausgeführt wird, hat einen Adressraum, der in die folgenden Segmente unterteilt ist:

Segment Beschreibung
0x00000000in0x0fffffff Enthält den Kernel.
0x10000000in0x1fffffff Enthält den Text des Anwendungsprogramms.
0x20000000in0x2fffffff Enthält die Anwendungsprogrammdaten, den Prozesszwischenspeicher und den Anwendungsstack.
0x30000000in0xcfffffff Verfügbar zur Verwendung durch gemeinsam genutzten Speicher oder mmap -Services.
0xd0000000in0xdfffffff Enthält gemeinsam genutzten Bibliothekstext.
0xe0000000in0xefffffff Verfügbar zur Verwendung durch gemeinsam genutzten Speicher oder mmap -Services.
0xf0000000in0xffffffff Enthält die gemeinsam genutzten Bibliotheksdaten der Anwendung.

Prozessadressraum in 64-Bit-Anwendungen

Ein 64-Bit-Anwendungsprogramm, das auf dem System ausgeführt wird, hat einen Adressraum, der in die folgenden Segmente unterteilt ist:

Segment Beschreibung
0x0000 0000 0000 0000in0x0000 0000 0fff ffff Enthält den Kernel.
0x0000 0000 f000 0000in0x0000 0000 ffff ffff Reserviert.
0x0000 0001 0000 0000in0x07ff ffff ffff ffff Enthält den Anwendungsprogrammtext, die Anwendungsprogrammdaten, den Prozessheapspeicher und gemeinsam genutzten Speicher oder mmap -Services.
0x0800 0000 0000 0000in0x08ff ffff ffff ffff Privat geladene Objekte.
0x0900 0000 0000 0000in0x09ff ffff ffff ffff Text und Daten der gemeinsam genutzten Bibliothek
0x0f00 0000 0000 0000in0x0fff ffff ffff ffff Anwendungsstack.
Hinweis: AIX® verwendet eine verzögerte Paging-Slot-Zuweisungstechnik für Speicher, der Anwendungen zugewiesen wird. Wenn einer Anwendung mit einer Subroutine wie mallocSpeicher zugeordnet wird, wird diesem Speicher erst dann ein Paging-Bereich zugeordnet, wenn der Speicher referenziert wird. Dieses Verfahren ist nützlich für Anwendungen, die große Sparse-Speichersegmente zuordnen. Dieses Verfahren kann sich jedoch auf die Portierbarkeit von Anwendungen auswirken, die sehr große Speichermengen zuordnen. Wenn die Anwendung erwartet, dass Aufrufe von malloc fehlschlagen, wenn nicht genügend Sicherungsspeicher zur Unterstützung der Speicheranforderung vorhanden ist, ordnet die Anwendung möglicherweise zu viel Speicher zu. Wenn später auf diesen Speicher verwiesen wird, reicht der Paging-Bereich der Maschine schnell aus, und das Betriebssystem beendet Prozesse, sodass das System den virtuellen Speicher nicht vollständig ausschöpft. Die Anwendung, die Speicher zuordnet, muss sicherstellen, dass Sicherungsspeicher für den zuzuordnenden Speicher vorhanden ist. Wenn Sie die Umgebungsvariable PSALLOC auf PSALLOC=early setzen, wird das Zuordnungsverfahren für Paging-Bereich in einen frühen Zuordnungsalgorithmus geändert. Bei einer frühen Zuordnung wird Paging-Bereich zugeordnet, sobald der Speicher angefordert wird. Weitere Informationen finden Sie unter Paging-Bereich und virtueller Speicher in Betriebssystem und Einheitenmanagement.

Erklärung der Systemzuordnungsrichtlinie

Die Zuordnungsrichtlinie bezieht sich auf die Gruppe von Datenstrukturen und Algorithmen, die verwendet werden, um den Heapspeicher darzustellen und die Zuordnung, Freigabe und Neuzuordnung zu implementieren. Das Subsystem malloc unterstützt verschiedene Zuordnungsrichtlinien, einschließlich der Standardzuordnungsrichtlinie, der Watson-Zuordnungsrichtlinie, der malloc 3.1 -Zuordnungsrichtlinie und der benutzerdefinierten Zuordnungsrichtlinie. Die API für den Zugriff auf das Subsystem malloc ist für alle Zuordnungsrichtlinien identisch; nur die zugrunde liegende Implementierung ist unterschiedlich.

Sie können die folgenden Umgebungsvariablen verwenden, um die Zuordnungsrichtlinie und alle normalen Optionen oder Debugoptionen für diese Richtlinie anzugeben:
  • MALLOCTYPE gibt die Zuordnungsrichtlinie an.
  • MALLOCOPTIONS gibt normale Optionen für die ausgewählte Zuordnungsrichtlinie an.
  • MALLOCDEBUG gibt Debugoptionen für die ausgewählte Zuordnungsrichtlinie an.
  • MALLOCALIGN gibt die malloc -Standardausrichtung außerhalb eines Programms an.

Die Standardzuordnungsrichtlinie ist im Allgemeinen effizienter und die bevorzugte Wahl für die meisten Anwendungen. Die anderen Zuordnungsrichtlinien weisen einige eindeutige Verhaltensmerkmale auf, die unter bestimmten Umständen von Vorteil sein können, wie im Abschnitt Verschiedene Zuordnungsrichtlinien vergleichenbeschrieben.

Einige Optionen für die verschiedenen Zuordnungsrichtlinien sind miteinander kompatibel und können zusammen verwendet werden. Wenn Sie Optionen zusammen verwenden, trennen Sie die durch die Umgebungsvariablen MALLOCOPTIONS und MALLOCDEBUG angegebenen Optionen durch ein Komma (,).

Die Umgebungsvariable MALLOCALIGN kann auf die gewünschte Standardausrichtung für jede malloc () -Zuordnung gesetzt werden. Beispiel:
MALLOCALIGN=16;  export MALLOCALIGN
Die Umgebungsvariable MALLOCALIGN kann auf eine beliebige Potenz von 2 Werten größer-gleich der Größe eines Zeigers im entsprechenden Ausführungsmodus (4 Byte für 32-Bit-Modus, 8 Byte für 64-Bit-Modus) gesetzt werden. Bei 32-Bit-Vektorprogrammen kann diese Umgebungsvariable auf 16 gesetzt werden, sodass alle malloc ()en bei Bedarf für Vektordatentypen geeignet ausgerichtet werden. Beachten Sie, dass 64-Bit-Vektorprogramme bereits 16 Byte ausgerichtete Zuordnungen empfangen.

Darüber hinaus kann das Programm intern für ein Programm die Routine mallopt (M_MALIGN, 16) verwenden, um die Standardeinstellung malloc () so zu ändern, dass 16 Byte ausgerichtete Zuordnungen bereitgestellt werden. Die Routine mallopt (M_MALIGN) ermöglicht einem Programm, die malloc-Standardausrichtung dynamisch zur Laufzeit zu steuern.

Informationen zur Standardzuordnungsrichtlinie

Die Standardzuordnungsrichtlinie verwaltet den freien Speicherplatz im Heapspeicher als Knoten in einer kartesischen binären Suchbaumstruktur, in der die Knoten von links nach rechts nach Adresse (steigende Adresse nach rechts) und von oben nach unten nach Länge angeordnet sind (sodass kein untergeordnetes Element größer als das übergeordnete Element ist). Diese Datenstruktur stellt keine Einschränkung für die Anzahl der Blockgrößen dar, die von der Baumstruktur unterstützt werden, und ermöglicht eine große Bandbreite potenzieller Blockgrößen. Verfahren zur Reorganisation von Baumstrukturen optimieren die Zugriffszeiten für Knotenposition, Einfügung und Löschung und schützen vor Fragmentierung.

Die Standardzuordnungsrichtlinie bietet Unterstützung für die folgenden optionalen Funktionen:

Zuordnung

Für die Bearbeitung einer Zuordnungsanforderung ist ein geringer Aufwand erforderlich. Dies ist auf die Notwendigkeit eines Metadatenpräfix und die Notwendigkeit einer geeigneten Ausrichtung jedes Speicherblocks zurückzuführen. Die Größe des Metadatenpräfix für alle Zuordnungen beträgt 8 und 16 Byte für 32 -Bit-bzw. 64-Bit-Programme. Jeder Block muss an einer 16-oder 32-Byte-Grenze ausgerichtet werden, sodass die Gesamtspeichermenge, die für eine Zuordnung der Größe n erforderlich ist, wie folgt lautet:

size = roundup(n + prefix_size, alignment requirement)

Eine Zuordnung der Größe 37 in einem 32-Bit-Prozess würde beispielsweise roundup(37 + 8, 16)erfordern, was 48 Byte entspricht.

Der Knoten der Baumstruktur mit der niedrigsten Adresse, die größer-gleich der erforderlichen Größe ist, wird aus der Baumstruktur entfernt. Wenn der gefundene Block größer als die benötigte Größe ist, wird der Block in zwei Blöcke unterteilt: einen der benötigten Größe und den zweiten einen Rest. Der zweite Block, der als runtbezeichnet wird, wird zur zukünftigen Zuordnung an die freie Baumstruktur zurückgegeben. Der erste Block wird an den Aufrufenden zurückgegeben.

Wenn kein Block mit ausreichender Größe in der freien Baumstruktur gefunden wird, wird der Heapspeicher erweitert, ein Block mit der Größe der angeforderten Erweiterung wird zur freien Baumstruktur hinzugefügt und die Zuordnung wird wie zuvor beschrieben fortgesetzt.

Freigabe

Speicherblöcke, die mit der Subroutine free freigegeben wurden, werden an die Baumstruktur im Stammverzeichnis zurückgegeben. Jeder Knoten entlang des Pfads zur Einfügemarke für den neuen Knoten wird untersucht, um festzustellen, ob er an den einzufügenden Knoten angrenzt. Ist dies der Fall, werden die beiden Knoten zusammengeführt und der neu zusammengeführte Knoten wird in die Baumstruktur verlagert. Wird kein angrenzender Block gefunden, wird der Knoten einfach an der entsprechenden Stelle im Baum eingefügt. Die Zusammenführung benachbarter Blöcke kann die Fragmentierung des Heapspeichers erheblich reduzieren.

Neuzuordnung

Wenn die Größe des neu angeordneten Blocks größer als der ursprüngliche Block ist, wird der ursprüngliche Block mit der Subroutine free an die freie Baumstruktur zurückgegeben, sodass eine mögliche Koaleszenz auftreten kann. Anschließend wird ein neuer Block mit der angeforderten Größe zugeordnet, die Daten werden vom ursprünglichen Block in den neuen Block verschoben und der neue Block wird an den Aufrufenden zurückgegeben.

Wenn die Größe des neu angeordneten Blocks kleiner als der ursprüngliche Block ist, wird der Block geteilt und der kleinere wird an den freien Baum zurückgegeben.

Einschränkungen

Die Standardzuordnungsrichtlinie unterstützt die folgenden Optionen:

Erklärung der Watson-Zuordnungsrichtlinie

Die Watson -Zuordnungsrichtlinie verwaltet den freien Speicherplatz im Heapspeicher als Knoten in zwei separaten rot-schwarzen Baumstrukturen: eine nach Adresse, die andere nach Größe sortiert. Rot-Schwarz-Bäume bieten einfachere und effizientere Baumoperationen als die kartesischen Bäume der Standardzuordnungsfunktion. Daher ist die Watson-Zuordnungsrichtlinie häufig schneller als die Standardrichtlinie.

Zuordnung

Die Watson -Zuordnungsrichtlinie hat dieselben Aufwandsanforderungen wie die Standardzuordnungsrichtlinie.

Die Größenbaumstruktur wird nach dem kleinstmöglichen Block durchsucht, der größer-gleich der erforderlichen Größe ist. Dieser Block wird dann aus der Größenbaumstruktur entfernt. Wenn der gefundene Block größer als die erforderliche Größe ist, wird der Block in zwei Blöcke unterteilt: einen Block der verbleibenden Größe und den zweiten Block der erforderlichen Größe. Der erste Block, der als runtbezeichnet wird, wird zur zukünftigen Zuordnung an die Größenbaumstruktur zurückgegeben. Der zweite Block wird an den Aufrufenden zurückgegeben. Wenn der in der Größenbaumstruktur gefundene Block genau die erforderliche Größe hatte, wird der Block sowohl aus der Größe als auch aus der Adressbaumstruktur entfernt und an den Aufrufenden zurückgegeben.

Wenn ein Block mit ausreichender Größe in der freien Baumstruktur nicht gefunden wird, wird der Prozessheapspeicher erweitert, ein Block mit der Größe dieser Erweiterung wird zur Größe und zur Adressbaumstruktur hinzugefügt und die Zuordnung wird wie zuvor beschrieben fortgesetzt.

Freigabe

Mit der Subroutine free freigegebene Speicherblöcke werden an die Adressbaumstruktur im Stammverzeichnis zurückgegeben. Jeder Knoten entlang des Pfads zur Einfügemarke für den neuen Knoten wird untersucht, um festzustellen, ob er an den einzufügenden Knoten angrenzt. Ist dies der Fall, werden die beiden Knoten zusammengeführt und der neu zusammengeführte Knoten wird in der Größenbaumstruktur verlagert. Wenn kein angrenzender Block gefunden wird, wird der Knoten einfach an der entsprechenden Stelle im Adressbaum und im Größenbaum eingefügt.

Nach dem Einfügen müssen beide rot-schwarzen Bäume auf korrekten Ausgleich überprüft werden.

Neuzuordnung

Wenn die Größe des neu zugeordneten Blocks größer als der ursprüngliche Block ist, wird der ursprüngliche Block mit der Subroutine free an die freien Bäume zurückgegeben, sodass eine mögliche Veralterung auftreten kann. Anschließend wird ein neuer Block mit der angeforderten Größe zugeordnet, die Daten werden vom ursprünglichen Block in den neuen Block verschoben und der neue Block wird an den Aufrufenden zurückgegeben.

Wenn die Größe des neu zuteilten Blocks kleiner als der ursprüngliche Block ist, wird der Block geteilt und der Rest an den freien Baum zurückgegeben.

Einschränkungen

Die Watson -Zuordnungsrichtlinie unterstützt die folgenden Optionen:

Erklärung der Zuordnungsrichtlinie für malloc 3.1

Die malloc 3.1 -Zuordnungsrichtlinie kann ausgewählt werden, indem vor dem Prozessstart MALLOCTYPE=3.1 festgelegt wird. Danach verwenden alle 32-Bit-Programme, die von der Shell ausgeführt werden, die Zuordnungsrichtlinie malloc 3.1 (64-Bit-Programme verwenden weiterhin die Standardzuordnungsrichtlinie).

Die malloc 3.1 -Zuordnungsrichtlinie verwaltet den Heapspeicher als Gruppe von 28 Hash-Buckets, die jeweils auf eine verlinkte Liste verweisen. Jede verknüpfte Liste enthält Blöcke einer bestimmten Größe. Der Index in den Hash-Buckets gibt die Größe der Blöcke in der verlinkten Liste an. Die Größe des Blocks wird nach folgender Formel berechnet:
size = 2 i + 4
Dabei gibt i das Bucket an. Dies bedeutet, dass die Blöcke in der durch Bucket 0 verankerten Liste 20 + 4 = 16 Byte lang sind. Wenn ein Präfix eine Größe von 8 Byte hat, können diese Blöcke daher Anforderungen für Blöcke zwischen 0 und 8 Byte erfüllen. Die folgende Tabelle zeigt, wie die angeforderten Größen auf die Buckets verteilt werden.
Hinweis: Dieser Algorithmus kann bis zu doppelt so viel Speicher verwenden, wie die Anwendung tatsächlich angefordert hat. Für Buckets, die größer als 4096 Byte sind, ist eine zusätzliche Seite erforderlich, da Objekte mit einer Seitengröße oder größer seitenorientiert sind. Da das Präfix unmittelbar vor dem Block steht, ist eine gesamte Seite nur für das Präfix erforderlich.
Bucket Blockgröße Zugeordnete Größen Verwendete Seiten
0 16 0 ... 8  
1 32 9 ... 24  
2 64 25 ... 56  
3 128 57 ... 120  
4 256 121 ... 248  
5 512 249 ... 504  
6 1K 505 ... 1K-8  
7 2K 1K-7 ... 2K-8  
8 4K 2K-7 ... 4K-8 2
9 8K 4K-7 ... 8K-8 3
10 16K 8K-7 ... 16K-8 5
11 32K 16K-7 ... 32K-8 9
12 64K 32K-7 ... 64K-8 17
13 128K 64K-7 ... 128K-8 33
14 256K 128K-7 ... 256K-8 65
15 512K 256K-7 ... 512K-8 129
16 1M 256K-7 ... 1M-8 257
17 2M 1M-7 ... 2M-8 513
18 4M 2M-7 ... 4M-8 1K + 1
19 8M 4M-7 ... 8M-8 2K + 1
20 16M 8M-7 ... 16M-8 4K + 1
21 32M 16M-7 ... 32M-8 8K + 1
22 64M 32M-7 ... 64M-8 16K + 1
23 128M 64M-7 ... 128M-8 32K + 1
24 256M 128M-7 ... 256M-8 64K + 1
25 512M 256M-7 ... 512M-8 128K + 1
26 1024M 512M-7 ... 1024M-8 256K + 1
27 2048M 1024M-7 ... 2048M-8 512K + 1

Zuordnung

Ein Block wird aus dem freien Pool zugeordnet, indem zuerst die angeforderten Byte unter Verwendung der folgenden Gleichung in einen Index im Bucketarray konvertiert werden:

needed = requested + 8

If needed <= 16,
then
bucket = 0

If needed > 16,
then
bucket = (log(needed)/log(2) rounded down to the nearest integer) - 3

Die Größe jedes Blocks in der durch das Bucket verankerten Liste beträgtblock size = 2 bucket + 4In: . Wenn die Liste im Bucket null ist, wird Speicher mit der Subroutine Sbrk zugeordnet, um der Liste Blöcke hinzuzufügen. Wenn die Blockgröße kleiner als eine Seite ist, wird mithilfe der Subroutine sbrk eine Seite zugeordnet und die Anzahl der Blöcke, die durch Division der Blockgröße in die Seitengröße erreicht wurden, wird zur Liste hinzugefügt. Wenn die Blockgröße größer-gleich einer Seite ist, wird der erforderliche Speicher mit der Subroutine sbrk zugeordnet und ein einzelner Block zur Liste der freien Blöcke für das Bucket hinzugefügt. Wenn die Liste der freien Blöcke nicht leer ist, wird der Block am Listenkopf an den Aufrufenden zurückgegeben. Der nächste Block in der Liste wird dann zum neuen Kopf.

Freigabe

Wenn ein Speicherblock an den freien Pool zurückgegeben wird, wird der Bucketindex wie bei der Zuordnung berechnet. Der freizugebende Block wird dann dem Kopf der Liste freier Blöcke für den Bucket hinzugefügt.

Neuzuordnung

Wenn ein Speicherblock neu zugeordnet wird, wird die erforderliche Größe mit der vorhandenen Größe des Blocks verglichen. Aufgrund der großen Größenvarianz, die von einem einzelnen Bucket verarbeitet wird, wird die neue Blockgröße häufig demselben Bucket wie die ursprüngliche Blockgröße zugeordnet. In diesen Fällen wird die Länge des Präfix aktualisiert, um die neue Größe wiederzugeben, und derselbe Block wird zurückgegeben. Wenn die erforderliche Größe größer als der vorhandene Block ist, wird der Block freigegeben, ein neuer Block aus dem neuen Bucket zugeordnet und die Daten aus dem alten Block in den neuen Block verschoben.

Einschränkungen

Die Einstellung MALLOCTYPE=3.1 aktiviert nur die malloc 3.1 -Richtlinie für 32-Bit-Programme. Damit 64-Bit-Programme die Richtlinie malloc 3.1 verwenden können, muss die Umgebungsvariable MALLOCTYPE explizit auf MALLOCTYPE=3.1_64BITgesetzt werden. Diese Zuordnungsrichtlinie ist weniger effizient als die Standardrichtlinie und wird in den meisten Fällen nicht empfohlen.

Die malloc 3.1 -Zuordnungsrichtlinie unterstützt die folgenden Optionen:

Informationen zur Poolzuordnungsrichtlinie

Der Pool Malloc ist ein Front-End mit hoher Leistung für die libc-Funktionen malloc, calloc, free, posix_memalign und realloc für die Verwaltung von Speicherobjekten kleiner als 513 Byte. Die Leistungsvorteile ergeben sich aus deutlich kürzeren Pfadlängen und einer besseren Auslastung des Datencache. Für Multithread-Anwendungen gibt es den weiteren Vorteil, dass lokale Thread-Pool-Anker verwendet werden, um atomare Operationen zu vermeiden. Dieses Front-End kann in Verbindung mit allen derzeit in libc (yorktownund watson) bereitgestellten Speicherverwaltungsschemata verwendet werden.

Führen Sie den folgenden Befehl aus, um den Pool malloc zu verwenden:
export MALLOCOPTIONS=pool<:max_size>

Wenn diese Option angegeben wird, wird während der malloc -Initialisierung eine Sammlung von Pools erstellt, wobei jeder Pool eine verknüpfte Liste von Objekten fester Größe ist. Der kleinste Pool kann Objekte mit Zeigergröße aufnehmen (z. B. 8 Byte für 32-Bit-Anwendungen oder 16 Byte für 64-Bit-Anwendungen). Jeder nachfolgende Pool kann Objekte aufnehmen, deren Größe größer als die des vorherigen Pools ist. Dies bedeutet, dass es 128 Pools für 32-Bit-Anwendungen und 64 Pools für 64-Bit-Anwendungen gibt. Die Poolsammlung wird als Array von Zeigern dargestellt, die die verknüpften Listen "verankern".

Der Pool Malloc verwendet seinen eigenen Speicher, den Poolheapspeicher, der nicht mit dem Standardspeicher mallocgemeinsam genutzt wird. Bei Angabe wird die Option max_size auf den nächsthöheren Wert von 2 MB aufgerundet und zur Steuerung der Größe des Poolheapspeichers verwendet. Die Option max_size kann als Dezimalzahl oder Hexadezimalzahl mit vorangestelltem 0x oder 0X angegeben werden ( export MALLOCOPTIONS=pool:0x1700000 setzt beispielsweise max_size nach dem Aufrunden auf 24 MB.

Für 32-Bit-Anwendungen beginnt die Größe des Poolzwischenspeichers bei 2 MB. Wenn mehr Speicher erforderlich ist und der Gesamtpoolheapspeicher kleiner als max_sizeist, werden weitere 2 MB angefordert. Jeder 2-MB-Bereich befindet sich auf einer 2-MB-Grenze, muss aber nicht an einen der anderen 2-MB-Bereiche angrenzen. Bei 64-Bit-Anwendungen wird ein einzelner zusammenhängender Poolheapspeicher von max_size während der malloc -Initialisierung zugeordnet und nie erweitert. Wird max_size nicht angegeben, wird standardmäßig 512 MB für 32-Bit-Anwendungen und 32 MB für 64-Bit-Anwendungen verwendet. Sowohl für den 32-als auch für den 64-Bit-Modus wird max_size auf 512 MB gesetzt, wenn eine größere Größe angegeben wird. For 32-bit mode, the Maximalgröße is set to 512MB, and for 64-bit mode, the Maximalgröße is set to 3.7 GB if a larger size is specified.

Speichernutzung

Alle Poolanker werden anfänglich auf NULL oder leer gesetzt. Wenn der Pool malloc eine Anforderung verarbeitet und der entsprechende Pool leer ist, wird eine Routine aufgerufen, die Speicher aus dem Poolheapspeicher in zusammenhängenden 1024-Byte-Blöcken an 1024-Byte-Grenzen zuordnet. Mehrere Objekte der angeforderten Größe werden "erstellt". Die erste Adresse wird zurückgegeben, um die Anforderung zu erfüllen, während die übrigen Objekte miteinander verknüpft und auf den Poolanker gestellt werden. Für jeden 1024-Byte-Chunk gibt es einen 2-Byte-Eintrag in einer Zusatztabelle, der von free verwendet wird, um die Größe eines zurückgegebenen Objekts zu ermitteln.

Wenn ein Objekt vom malloc -Pool freigegeben wird, wird es lediglich auf den entsprechenden Poolanker "übertragen". Es wird nicht versucht, Blöcke zu verbinden, um größere Objekte zu erstellen.

Aufgrund dieses Verhaltens kann malloc -Pool mehr Speicher als andere Formen von mallocbelegen.

Alignment

Die Standardausrichtung für die Subroutinen malloc (), calloc ()und realloc () muss angegeben werden, indem die Umgebungsvariable MALLOCALIGN entsprechend festgelegt wird. Die Subroutine posix_memalign () wird weiter ausgeführt, auch wenn die Umgebungsvariable MALLOCALIGN nicht gesetzt ist. Wenn MALLOCALIGN größer als 512 ist, wird der malloc -Pool nicht verwendet.

Cacheeffizienz

Die mit malloc pool zugeordneten Speicherobjekte haben keine Präfixe oder Suffixe. Datencachezeilen werden daher dichter mit anwendungsverwendbaren Daten gepackt. Da alle Speicherobjekte mit einer Größe von 2 Potenz an einem Grenzwert dieser Größe ausgerichtet sind, ist jedes Objekt in der minimalen Anzahl von Cachezeilen enthalten. Die Subroutinen malloc und free durchsuchen keine Baumstrukturen oder verlinkten Listen und "belasten" daher nicht den Cache.

Multithread-Unterstützung

Malloc -Pools können die Leistung in einem Multithread-Szenario erheblich verbessern, da sie Sperrenkonflikte und die Notwendigkeit atomarer Operationen reduzieren.

Unterstützung für Lastausgleich

In einigen Multithread-Szenarien kann der freie Pool eines Threads sehr groß werden, weil sich der dynamisch zugeordnete Speicher wiederholt. Andere Threads sind jedoch möglicherweise nicht in der Lage, diesen Speicher zu nutzen.

Die Lastausgleichsunterstützung bewirkt, dass ein Thread die Hälfte des Speichers in jedem Pool an einen globalen Pool freigibt, nachdem der Pool einen Grenzwert erreicht hat, damit andere Threads ihn verwenden können. Sie können die Schwellenwerte optimieren, bei denen der Pool eines Threads neu justiert wird.

Um die Lastausgleichsunterstützung zu aktivieren, müssen die folgenden Optionen exportiert werden:

export MALLOCOPTIONS=pool:0x80000000,pool_balanced
export MALLOCFREEPOOL=min_size<-max_size>:threshold_value<,min_size<-max_size>:
threshold_value, ... >,default:threshold
Im folgenden Beispiel wird der Schwellenwert für die Pools, die einen Speicher von 0 bis 16 Byte und 256 Chunks bereitstellen, und der Schwellenwert des Pools, der 32-Byte-Chunks bedient, auf 512-Byte-Chunks gesetzt. Für die übrigen Pools sind Blöcke mit 128 Byte der Schwellenwert.
export MALLOCFREEPOOL=0-16:256,32:512,default:128

Debugging-Unterstützung

Es gibt keine Debugversion dieses leistungsfähigen Front-Ends. Wenn die Umgebungsvariable MALLOCDEBUG gesetzt ist, wird die Pooloption ignoriert. Es wird erwartet, dass das Debugging für Anwendungen mit "normal" malloc durchgeführt wird, bevor das Pooling aktiviert wird.

Erklärung der benutzerdefinierten Zuordnungsrichtlinie

Das malloc-Subsystem bietet einen Mechanismus, über den Benutzer eigene Algorithmen für die Verwaltung des Systemheapspeichers und die Speicherzuordnung entwickeln können.

Informationen zur Option 'no_overwrite'

Eine zusätzliche Option für alle Zuordnungsrichtlinien ist no_overwrite. Um den Aufwand für den Glink-Code im malloc-Subsystem zu reduzieren, wird der Funktionsdeskriptor für die malloc -Subsystem-APIs mit dem Funktionsdeskriptor für die eigentliche zugrunde liegende Implementierung überschrieben. Da einige Programme, wie z. B. Debugger anderer Anbieter, möglicherweise nicht funktionieren, wenn Funktionszeiger auf diese Weise geändert werden, kann diese Optimierung mit der Option no_overwrite inaktiviert werden.

Um diese Optimierung zu inaktivieren, legen Sie MALLOCOPTIONS=no_overwrite vor dem Prozessstart fest.

Verschiedene Zuordnungsrichtlinien vergleichen

Die verschiedenen oben ausgearbeiteten malloc-Zuordnungsrichtlinien bieten Anwendungsentwicklern Flexibilität, wenn sie separat oder auf unterstützte Weise kombiniert verwendet werden. Es liegt in der Verantwortung des Entwicklers, die eindeutigen Anforderungen einer Anwendung zu erkennen und die verschiedenen Parameter der Zuordnungsrichtlinie auf vorteilhafte Weise zu optimieren.

Standard-und malloc 3.1 -Zuordnungsrichtlinien vergleichen

Da die Zuordnungsrichtlinie malloc 3.1 die Größe jeder Zuordnungsanforderung auf die nächste Potenz von 2 aufrundet, kann sie zu einer beträchtlichen Fragmentierung des virtuellen und des Realspeichers und einer schlechten Referenzlokalität führen. Die Standardzuordnungsrichtlinie ist im Allgemeinen eine bessere Wahl, da sie genau die angeforderte Speichermenge zuordnet und effizienter ist, wenn zuvor verwendete Speicherblöcke freigegeben werden.

Leider können einige Anwendungsprogramme versehentlich von Nebeneffekten der malloc 3.1 -Zuordnungsrichtlinie abhängen, um eine akzeptable Leistung oder sogar eine korrekte Funktion zu erzielen. Beispielsweise kann ein Programm, das das Ende eines Arrays überläuft, ordnungsgemäß funktionieren, wenn die Zuordnungsfunktion malloc 3.1 nur aufgrund des zusätzlichen Speicherplatzes verwendet wird, der vom Aufrundungsprozess bereitgestellt wird. Dasselbe Programm ist wahrscheinlich unberechenbar oder schlägt sogar fehl, wenn es mit der Standardzuordnungsfunktion verwendet wird, weil die Standardzuordnungsfunktion nur die Anzahl der angeforderten Bytes zuordnet.

Ein weiteres Beispiel: Aufgrund der ineffizienten Speicherplatzfreigabe des malloc 3.1 -Zuordnungsalgorithmus erhält das Anwendungsprogramm fast immer Speicherbereich, der auf Nullen gesetzt wurde (wenn ein Prozess eine bestimmte Seite im Arbeitssegment zum ersten Mal berührt, wird diese Seite auf Nullen gesetzt). Anwendungen können von diesem Nebeneffekt für die korrekte Ausführung abhängig sein. Tatsächlich ist das Überschreiben mit Nullen aus dem zugeordneten Speicherbereich keine angegebene Funktion der Subroutine malloc und würde zu einer unnötigen Leistungseinbuße für Programme führen, die nur als erforderlich und möglicherweise nicht als Nullen initialisiert werden. Da die Standardzuordnungsfunktion die Wiederverwendung von Speicherplatz aggressiver angeht, schlagen Programme, die vom Empfang von Speicher mit Nullen von malloc abhängig sind, wahrscheinlich fehl, wenn die Standardzuordnungsfunktion verwendet wird.

Wenn ein Programm eine Struktur kontinuierlich einer etwas größeren Größe zuordnet, muss die Zuordnungsfunktion malloc 3.1 die Struktur möglicherweise nicht sehr häufig verschieben. In vielen Fällen kann die Subroutine realloc den zusätzlichen Speicherbereich nutzen, der durch das implizite Runden im Zuordnungsalgorithmus malloc 3.1 bereitgestellt wird. Die Standardzuordnungsfunktion muss die Struktur normalerweise auf einen etwas größeren Bereich verschieben, da die Wahrscheinlichkeit besteht, dass etwas anderes von der Subroutine malloc direkt darüber aufgerufen wurde. Dies kann die Darstellung einer Verschlechterung der Leistung der Subroutine realloc darstellen, wenn die Standardzuordnungsfunktion anstelle der Zuordnungsfunktion malloc 3.1 verwendet wird. In Wirklichkeit ist es das Auftreffen von Kosten, die in der Struktur des Anwendungsprogramms implizit sind.

Debugging application mismanagement of the system heap

Das Subsystem malloc bietet eine Sammlung von Debugging-Tools, die dem Anwendungsentwickler helfen sollen, Fehler im Heapspeichermanagement eines Programms zu beheben. Diese Debugging-Tools werden über die Umgebungsvariable MALLOCDEBUG gesteuert.

Zusammenfassung der malloc-Umgebungsvariablen und -Optionen

Die folgende Tabelle zeigt die Kompatibilität zwischen den Umgebungsvariablen MALLOCTYPE und MALLOCOPTIONS .
Tabelle 1 Kompatibilität zwischen MALLOCTYPE und MALLOCOPTIONS Umgebungsvariablen
  Multiheap (und Unteroptionen) Buckets (und Unteroptionen) Thread-Cache ablehnen Nicht überschreiben
Standardzuordnungsfunktion ja ja ja ja ja
3.1 nein nein ja ja ja
Watson nein nein nein nein nein
Watson2 nein nein nein nein nein
Benutzer: nein nein nein nein ja
Tabelle 2. Kompatibilität zwischen MALLOCDEBUG und MALLOCTYPE Umgebungsvariablen
  York Town < Standardzuordnungsfunktion > 3.1 Watson Watson2 Benutzer:
catch_overflow (und Unteroptionen) ja nein ja ja nein
Berichtszuordnungen ja nein ja ja nein
'postfree_checking' ja nein ja ja nein
validate_ptrs ja nein ja ja nein
trace ja nein ja ja nein
log ja nein ja ja nein
verbose nein nein nein nein nein
Alle MALLOCDEBUG -Optionen sind mit MALLOCOPTIONSkompatibel und werden unterstützt.

Informationen zur Watson2 -Zuordnungsrichtlinie

Das malloc-Subsystem Watson2 passt sich dem Verhalten der Anwendung an, wenn es von einem Einzelthread zu mehreren Threads und von mehreren Threads zu einem Einzelthread wechselt. Sie verwendet einen threadspezifischen Mechanismus, der eine variierende Anzahl von Heapspeicherstrukturen verwendet, die vom Verhalten des Programms abhängig sind. Daher sind keine Konfigurationsoptionen erforderlich. Das malloc-Subsystem Watson2 hat die Kosten pro Operation für viele Workloads amortisiert (logN), da eine große Anzahl von Operationen zu einer konstanten Zeit ohne Synchronisation ausgeführt werden kann.

Zuordnung

Die Zuordnung erfolgt über eine Kombination von Mechanismen. Diese Mechanismen hängen von Parametern ab, z. B. der Anzahl aktiver Threads, der Größe der Anforderung und dem Freigabeprotokoll des Prozesses. Die Gruppe der Mechanismen reicht von einem threadspezifischen Caching und verwendet eine variable Anzahl von Heapspeichern, die Threadaffinität zu einer doppelt rot-schwarzen Baumstruktur und einer seitenbasierten Verbindung hat.

Freigabe

Die Freigabe hängt von denselben Parametern wie das Zuordnungsverhalten ab. Normalerweise wird ein Rückgabeblock im threadspezifischen Cache erfasst. Je nach Heapspeicheraffinität und Kapazitätsauslastung kann Speicher an eine der mehreren Heapspeicherstrukturen zurückgegeben werden. Manchmal wird der Inhalt der Struktur mit mehreren Heap-Speicherstrukturen in einer gemeinsamen Heap-Speicherstruktur konsolidiert, um die Verbindung zu verbessern und die Heap-Fragmentierung zu reduzieren. Um die Zuverlässigkeit gegenüber Anwendungsfehlern zu verbessern, identifiziert der Zuordnungsfunktion die Freigabe ungültiger Zeiger oder beschädigter Blöcke bis zu einem bestimmten Grad und filtert diese Operationen.

Neuzuordnung

Große Speicherblöcke, die ausreichend sind, werden wiederverwendet. Wenn der aktuelle Block die Anforderung nicht erfüllen kann, wird er durch eine reguläre Freigabe und Zuordnung ersetzt.

Einschränkungen

Das malloc-Subsystem Watson2 ist für die Anwendung adaptiv und erfordert keine weiteren Optionen, aber das malloc-Subsystem Watson2 unterstützt die folgenden Debugfunktionen, die von der Variablen MALLOCDEBUG gesteuert werden: validate_ptrs, report_allocations, und trace. Die Berichte, die sich auf Zuordnungen beziehen, können mithilfe der Option output:<filename> in eine Datei umgeleitet werden. Ausführliche Informationen zur Variablen MALLOCDEBUG finden Sie unter Debug malloc tool .