Speicherverwaltung ist eine entscheidende Komponente der Computersoftwareentwicklung und hat die Aufgabe, den Speicher in Anwendungen effektiv zuzuweisen, zu nutzen und freizugeben. Seine Bedeutung liegt in der Verbesserung der Softwareleistung und der Gewährleistung der Systemstabilität.
Garbage Collection (GC) ist in modernen Programmiersprachen wie Java und Go von zentraler Bedeutung. Es erkennt und recycelt ungenutzten Speicher selbstständig, sodass Entwickler den Speicher nicht mehr manuell verwalten müssen. Das Konzept von GC entstand ursprünglich in den späten 1950er Jahren in der Programmiersprache LISP und markierte die Einführung der automatisierten Speicherverwaltung.
Zu den wichtigsten Vorteilen der automatisierten Speicherverwaltung gehören:
Es ist wichtig, die Natur des „Mülls“ im Gedächtnis zu verstehen und rückgewinnbaren Speicherplatz zu identifizieren. In den kommenden Kapiteln werden wir zunächst die Grundprinzipien der Speicherbereinigung untersuchen.
Der Referenzzählalgorithmus weist ein Feld im Header des Objekts zu, um seinen Referenzzähler zu verfolgen. Dieser Wert erhöht sich mit jedem neuen Verweis und verringert sich, wenn ein Verweis entfernt wird. Wenn die Anzahl Null erreicht, ist das Objekt für die Garbage Collection geeignet.
Bedenken Sie den folgenden Code:
Erstellen Sie zunächst einen String mit dem Wert demo, auf den durch d verwiesen wird (Abbildung 1).
String d = new String("demo");
Abbildung 1 – Nachdem ein String erstellt wurde
Setzen Sie dann d auf null. Der Referenzzähler der Demo ist Null. Im Referenzzählalgorithmus soll der Speicher für Demo zurückgewonnen werden (Abbildung 2).
d =null; // Reference count of 'demo' becomes zero, prompting garbage collection.
Abbildung 2 – Wenn die Referenz ungültig wird
Der Referenzzählalgorithmus arbeitet während der Programmausführung und vermeidet Stop-The-World-Ereignisse, die das Programm vorübergehend zur Speicherbereinigung anhalten. Der größte Nachteil besteht jedoch darin, dass Zirkelbezüge nicht verarbeitet werden können (Abbildung 3).
Zum Beispiel:
public class CircularReferenceDemo { public CircularReferenceDemo reference; private String name; public CircularReferenceDemo(String name) { this.name = name; } public void setReference(CircularReferenceDemo ref) { this.reference = ref; } public static void main(String[] args) { CircularReferenceDemo objA = new CircularReferenceDemo("Ref_A"); CircularReferenceDemo objB = new CircularReferenceDemo("Ref_B"); objA.setReference(objB); objB.setReference(objA); objA = null; objB = null; } }
Hier verhindern die gegenseitigen Referenzen zwischen objA und objB trotz der Aufhebung externer Referenzen deren Garbage Collection.
Abbildung 3 – Zirkelbezüge
Wir sehen, dass auf beide Objekte nicht mehr zugegriffen werden kann. Sie werden jedoch voneinander referenziert und daher wird ihr Referenzzähler niemals Null sein. Folglich wird der GC-Kollektor niemals aufgefordert, sie mithilfe des Referenzzählungsalgorithmus in den Müll zu sammeln.
Dieser Algorithmus wird praktisch in C++ durch die Verwendung von std::shared_ptr implementiert. std::shared_ptr wurde für die Verwaltung des Lebenszyklus dynamisch zugewiesener Objekte entwickelt und automatisiert das Erhöhen und Verringern von Referenzzählern, wenn Zeiger auf das Objekt erstellt oder zerstört werden. Dieser intelligente Zeiger ist Teil der C++-Standardbibliothek und bietet robuste Speicherverwaltungsfunktionen, die die mit der manuellen Speicherverwaltung verbundenen Risiken erheblich verringern. Immer wenn ein std::shared_ptr kopiert wird, erhöht sich der interne Referenzzähler des verwalteten Objekts und spiegelt die neue Referenz wider. Wenn umgekehrt ein std::shared_ptr zerstört wird, den Gültigkeitsbereich verlässt oder einem anderen Objekt neu zugewiesen wird, verringert sich der Referenzzähler. Der zugewiesene Speicher wird automatisch zurückgefordert und das Objekt wird zerstört, wenn sein Referenzzähler Null erreicht,
Speicherlecks werden effektiv verhindert, indem sichergestellt wird, dass kein Objekt ohne Notwendigkeit zugewiesen bleibt.
Der Algorithmus zur Erreichbarkeitsanalyse beginnt an den GC-Wurzeln und durchläuft den Objektgraphen. Objekte, die von diesen Wurzeln aus nicht erreicht werden können, gelten als nicht wiederherstellbar und werden gezielt eingesammelt.
Wie im Bild unten gezeigt, sollten die Objekte im blauen Kreis am Leben gehalten werden und die Objekte im grauen Kreis können recycelt werden (Abbildung 4).
Abbildung 4 – Speicherverlust
Diese Methode löst effektiv das Problem der Zirkelverweise, die dem Referenzzählalgorithmus innewohnen. Objekte, die von den GC-Wurzeln aus nicht erreichbar sind, werden zur Sammlung kategorisiert.
Typischerweise umfassen Java-Objekte, die als GC-Wurzeln gelten:
GraalVM bietet einen AOT-Compiler (Ahead-of-Time), der Java-Anwendungen in eigenständige ausführbare Binärdateien übersetzt, die als GraalVM Native Images bekannt sind. Diese Binärdateien wurden von Oracle Labs entwickelt
Kapseln Sie Anwendungs- und Bibliotheksklassen sowie Laufzeitkomponenten wie den GC und ermöglichen Sie so Vorgänge ohne Java Runtime Environment (JRE).
Der Prozess umfasst eine statische Analyse zur Bestimmung erreichbarer Komponenten, die Initialisierung durch ausgeführte Blöcke und den Abschluss durch die Erstellung eines Snapshots des Anwendungsstatus für die anschließende Maschinencode-Übersetzung.
Die Substrate VM ist ein integraler Bestandteil der GraalVM-Suite, orchestriert von Oracle Labs. Es handelt sich um eine erweiterte JVM, die nicht nur die AOT-Kompilierung (Ahead-of-Time) unterstützt, sondern auch die Ausführung von Sprachen jenseits von Java, wie JavaScript, Python, Ruby und sogar nativen Sprachen wie C und C++, erleichtert. Im Kern dient Substrate VM als hochentwickeltes Framework, das es GraalVM ermöglicht, Java-Anwendungen in eigenständige native Binärdateien zu kompilieren. Diese Binärdateien sind für ihre Ausführung nicht auf eine herkömmliche Java Virtual Machine (JVM) angewiesen, was die Bereitstellung optimiert und
betriebliche Prozesse.
Eines der Hauptmerkmale von Substrate VM ist sein spezieller Garbage Collector, der speziell auf Anwendungen abgestimmt ist, die eine geringe Latenz und einen minimalen Speicherbedarf erfordern. Dieser Garbage Collector beherrscht das einzigartige Speicherlayout und Betriebsmodell nativer Images, die sich erheblich von herkömmlichen Java-Anwendungen unterscheiden, die auf einer Standard-JVM ausgeführt werden. Das Fehlen eines Just-In-Time (JIT)-Compilers in nativen Substrate VM-Images ist eine strategische Entscheidung, die dazu beiträgt, die Gesamtgröße der ausführbaren Datei zu minimieren. Dies liegt daran, dass dadurch die Notwendigkeit entfällt, den JIT-Compiler und die zugehörigen Metadaten einzubeziehen, deren Umfang und Komplexität erheblich sind.
Obwohl GraalVM mit Java entwickelt wird, führt dies außerdem zu bestimmten Einschränkungen, insbesondere im Hinblick auf den nativen Speicherzugriff. Solche Einschränkungen sind in erster Linie auf Sicherheitsbedenken und die Notwendigkeit zurückzuführen, die Kompatibilität zwischen verschiedenen Plattformen aufrechtzuerhalten. Der Zugriff auf den nativen Speicher ist jedoch für optimale Garbage-Collection-Vorgänge unerlässlich. Um dieses Problem zu lösen, verwendet Substrate VM eine Reihe spezialisierter Schnittstellen, die sichere und effiziente Interaktionen mit dem nativen Speicher ermöglichen. Diese Schnittstellen sind Teil der umfassenderen GraalVM-Architektur und ermöglichen es Substrate VM, den Speicher ähnlich wie niedrigere Sprachen wie C effektiv zu verwalten und dabei die Sicherheit und Verwaltbarkeit von Java beizubehalten.
In der Praxis machen diese Fähigkeiten Substrate VM zu einem äußerst vielseitigen Tool, das die Funktionalität und Effizienz von mit GraalVM kompilierten Anwendungen verbessert. Indem wir es Entwicklern erlauben
Durch die Nutzung eines breiteren Spektrums an Programmiersprachen und deren Kompilierung in effiziente native Binärdateien verschiebt Substrate VM die Grenzen dessen, was mit herkömmlichen Java-Entwicklungsumgebungen erreicht werden kann. Dies macht es zu einem unschätzbaren Vorteil für moderne Softwareentwicklungsprojekte, die hohe Leistung, reduzierten Ressourcenverbrauch und vielseitige Sprachunterstützung erfordern.
Bemerkenswerte Elemente von Substrate VM sind:
Vereinfachter Speicherzugriff über Schnittstellen wie Pointer Interface Pointer für Rohspeicheroperationen und WordBase Interface WordBase für die Verarbeitung wortgroßer Werte.
Aufteilung des Heaps in vorinitialisierte Segmente, die unveränderliche Objekte enthalten, und Laufzeitsegmente für die dynamische Objektzuweisung (Abbildung 5).
Abbildung 5 – Speicherverwaltung im nativen Image
Zur Laufzeit enthält der sogenannte Image-Heap in Substrate VM Objekte, die während des Image-Erstellungsprozesses erstellt wurden. Dieser Abschnitt des Heaps ist mit Daten aus dem Datenabschnitt der ausführbaren Binärdatei vorinitialisiert und beim Start der Anwendung leicht zugänglich. Die im Bild-Heap befindlichen Objekte gelten als unsterblich; Daher werden Referenzen innerhalb dieser Objekte von
als Stammzeiger behandelt
Müllsammler. Der GC durchsucht jedoch nur Teile des Bildheaps nach Stammzeigern, insbesondere solchen, die nicht als schreibgeschützt markiert sind.
Während des Erstellungsprozesses werden als schreibgeschützt gekennzeichnete Objekte in einem bestimmten schreibgeschützten Abschnitt des Image-Heaps platziert. Da diese Objekte niemals Verweise auf zur Laufzeit zugewiesene Objekte enthalten, enthalten sie keine Stammzeiger, sodass der GC sie bei Scans umgehen kann. Ebenso fehlen Objekten, die ausschließlich aus primitiven Daten oder Arrays primitiver Typen bestehen, Wurzelzeiger. Dieses Attribut rationalisiert den Garbage-Collection-Prozess weiter, da diese Objekte bei GC-Scans weggelassen werden können.
Im Gegensatz dazu ist der Java-Heap für die Aufnahme gewöhnlicher Objekte vorgesehen, die während der Laufzeit dynamisch erstellt werden. Dieser Teil des Heaps unterliegt der regelmäßigen Garbage Collection, um Speicherplatz freizugeben, der von Objekten belegt wird, die nicht mehr verwendet werden. Es ist als Generationenhaufen mit Alterungsmechanismen strukturiert, die eine effiziente Speicherverwaltung im Laufe der Zeit ermöglichen.
Diese Trennung zwischen dem vorinitialisierten, unsterblichen Image-Heap und dem dynamisch verwalteten Java-Heap ermöglicht es Substrate VM, die Speichernutzung und die Effizienz der Speicherbereinigung zu optimieren und dabei sowohl statische als auch dynamische Aspekte der Anwendungsspeicheranforderungen zu berücksichtigen.
Im Heap-Modell von Substrate VM ist der Speicher systematisch in Strukturen organisiert, die als Heap-Chunks bezeichnet werden. Diese Blöcke, die standardmäßig eine Größe von 1024 KB haben, bilden ein kontinuierliches Segment des virtuellen Speichers, das ausschließlich der Objektspeicherung zugewiesen ist. Die Organisationsstruktur dieser Blöcke ist eine verknüpfte Liste, wobei der Endblock das zuletzt hinzugefügte Segment darstellt. So ein Modell
ermöglicht eine effiziente Speicherzuweisung und Objektverwaltung.
Diese Heap-Blöcke werden weiter in zwei Typen eingeteilt: ausgerichtet und nicht ausgerichtet. Ausgerichtete Heap-Blöcke können mehrere Objekte kontinuierlich enthalten. Diese Ausrichtung ermöglicht eine einfachere Zuordnung von
Objekte in ihre jeweiligen übergeordneten Heap-Blöcke, wodurch die Speicherverwaltung intuitiver und effizienter wird. In Szenarien, in denen eine Objektheraufstufung erforderlich ist – typischerweise während der Garbage Collection und
Speicheroptimierung – ein Objekt wird von seiner ursprünglichen Platzierung in einem übergeordneten Heap-Block in einen Ziel-Heap-Block verschoben, der sich in einem bestimmten „alten To-Space“ befindet. Diese Migration ist Teil der generationsübergreifenden Heap-Management-Strategie, die zur Optimierung des Garbage-Collection-Prozesses beiträgt, indem junge von alten Objekten getrennt werden, wodurch der Overhead während der GC-Zyklen reduziert wird.
GraalVM Native Image unterstützt verschiedene GCs, die auf unterschiedliche Anforderungen zugeschnitten sind:
Serieller GC: Standardmäßiger Kollektor mit geringem Platzbedarf, geeignet für Single-Threaded-Anwendungen.
G1 Garbage Collector: Entwickelt für Multithread-Anwendungen mit großen Heap-Größen, was die Flexibilität bei der Generationsverwaltung erhöht.
Epsilon GC: Ein minimalistischer Kollektor, der die Zuweisung übernimmt, aber keine Rückgewinnung bietet und am besten für kurzlebige Anwendungen verwendet wird, bei denen die vollständige Heap-Auslastung vorhersehbar ist.
Zusammenfassend lässt sich sagen, dass Substrate VM die Speicherverwaltung innerhalb von GraalVM effektiv optimiert, indem fortschrittliche Techniken wie spezielle Garbage Collection und strukturierte Heap-Verwaltung integriert werden. Diese Funktionen, einschließlich Heap-Chunks und separate Speichersegmente für Image- und Java-Heaps, optimieren die Speicherbereinigung und verbessern die Anwendungsleistung. Da Substrate VM eine Vielzahl von Programmiersprachen unterstützt und diese in effiziente native Binärdateien kompiliert, zeigt es, wie moderne JVM-Frameworks über traditionelle Grenzen hinausgehen können, um die Ausführungseffizienz und Robustheit in verschiedenen Anwendungsumgebungen zu verbessern. Dieser Ansatz setzt einen hohen Standard für zukünftige Entwicklungen in der Technologie virtueller Maschinen und der Anwendungsbereitstellung.
Das obige ist der detaillierte Inhalt vonSpeicherverwaltung im GraalVM Native Image. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!