Über Speicherverwaltung in Java und C ++

8

Nun, mir wurde eine Aufgabe zugewiesen, im Grunde herauszufinden, wie die Speicherzuweisung für jede Sprache funktioniert, die ich verwenden werde. Nach einigen Nachforschungen habe ich einige Fragen und Zweifel, auf die ich gerne einen Einblick bekommen würde. Zum Beispiel:

Ich lese hier , dass Java genau angibt, wie der Stack-Inhalt organisiert ist. Betrachtet man die JVM-Spezifikationsstruktur , sagt sie im Wesentlichen den Stack enthält Rahmen, und die Rahmen enthalten alles, was innerhalb der Klasse ist, indem die Variablen und Funktionen richtig zugewiesen werden. Vielleicht verpasse ich hier etwas, aber ich verstehe nicht, wie das anders ist als was C ++ tut. Ich frage, weil der erste Link sagt, dass die Java-Spezifikation des Stack-Inhalts Compiler-Inkompatibilitäten vermeidet.

Außerdem muss ich noch herausfinden, wie die Speichersegmente genau übereinander angeordnet sind. Zum Beispiel weiß ich, dass der Speicher in globale Variablen unterteilt ist, Aufruf-Stack, Heap und Code für C ++, aber ich weiß nicht, ob die Adresse des Heaps höher ist als die des Stacks oder ob das von der Implementierung abhängt. Ich frage mich auch, ob ein Java-Programm mehr als das hat und wie es auch angelegt wäre. Ich stelle mir vor, dass es einen Standard gibt, da die JVM wissen muss, wo alles ist, um sie zu benutzen, obwohl ich denke, dass sie nur die Zeiger haben und den Rest dem OS überlassen kann. Ich stelle mir auch vor, dass es zumindest einen De-facto-Standard geben muss.

Eine weitere Sache, die ich nicht verstehe, ist der Laufzeit-Konstanten-Pool. Es sollte "eine pro-Klasse- oder pro-Schnittstelle-Laufzeitdarstellung der Tabelle" constant_pool "in einer Klassendatei sein", aber ich glaube nicht, dass ich verstehe, was es tut. Es scheint ein Tag zu haben, um anzuzeigen, um welchen Typ es sich handelt. Dann der Name der Struktur (vom Programmierer gegeben oder vom zugrundeliegenden System zugewiesen?). Dann scheint es, dass der Rest davon mit dem, was das Tag beschreibt (ein Thread, ein Array, etc.) variiert.

Wenn meine Interpretation des Laufzeitkonstantenpools richtig ist, warum sind sie dann ebenso notwendig wie Stapelrahmen? Liegt es daran, dass Stack-Frames nur für die Stack-Segmente sorgen und der Laufzeit-Constant-Pool auch Zeiger für den Heap-Speicher haben muss?

    
Bill the Lizard 14.01.2009, 00:05
quelle

2 Antworten

6
  

Betrachtet man die Struktur der JVM-Spezifikation, so sagt sie im Grunde, dass der Stapel Frames enthält und dass die Frames alles enthalten, was in der Klasse enthalten ist, indem man die Variablen und Funktionen richtig zuordnet. Vielleicht verpasse ich hier etwas, aber ich verstehe nicht, wie das anders ist als was C ++ tut. Ich frage, weil der erste Link sagt, dass die Java-Spezifikation des Stack-Inhalts Compiler-Inkompatibilitäten vermeidet.

In der Praxis folgen C ++ - Compiler derselben grundlegenden Strategie. Es wird jedoch vom Normenausschuss nicht als Sprachproblem angesehen. Stattdessen folgen C ++ - Compiler diesem System, weil so die meisten CPUs und Betriebssysteme entworfen werden. Die verschiedenen Plattformen sind sich nicht einig darüber, ob Daten an Funktionen auf einem Stack oder über Register (RISC-Maschinen) weitergegeben werden, ob der Stack auf- oder abwächst, ob verschiedene Aufrufkonventionen "normale" Aufrufe des Stacks erlauben und andere, um etwas zu verwenden sonst (z. B. __fastcall und nackt ), ob es so etwas wie verschachtelte Funktionen , Tail Call-Unterstützung , etc.

Tatsächlich ist es für einen übereinstimmenden C ++ - Compiler möglich, zu etwas wie einer Schema-VM zu kompilieren, wobei "der Stapel" sich sehr unterscheidet, weil Schema Implementierungen erfordert, um sowohl Endanrufe als auch Fortsetzungen zu unterstützen. So etwas habe ich noch nie gesehen, aber es wäre legal.

Die "Compiler Inkompatibilitäten" sind am offensichtlichsten, wenn Sie versuchen, ein zu schreiben Müllsammler :

  

Alle lokalen Variablen, sowohl für die aktuelle Funktion als auch für alle Aufrufer, befinden sich in ["the" stack, aber berücksichtigen Sie ucontext.h und Windows Fibers ]. Für jede Plattform (dh OS + CPU + Compiler) gibt es eine Möglichkeit herauszufinden, wo ["der" Stack] ist. Tamarin macht das, dann scannt es während GC alle diese Erinnerungen, um zu sehen, wohin die Einheimischen zeigen. ...

     

Diese Magie lebt in einem Makro, MMGC_GET_STACK_EXTENTS, definiert in der Kopfzeile MMgc / GC.h. ... [T] hier ist eine separate Implementierung für jede Plattform.

     

Zu irgendeinem Zeitpunkt befinden sich einige Locals möglicherweise in CPU-Registern und nicht auf dem Stack. Um dies zu bewältigen, verwendet das Makro einige Zeilen Assemblercode, um den Inhalt aller Register auf den Stapel zu schreiben. Auf diese Weise kann MMgc einfach den Stapel durchsuchen und alle lokalen Variablen sehen.

Außerdem werden Objekte in Java normalerweise nicht auf dem Stapel zugeordnet. Stattdessen sind Referenzen auf sie. Ints, Doubles, Booleans und andere primitive Typen werden auf dem Stack zugewiesen. In C ++ kann alles auf dem Stack zugeordnet werden, was eine eigene Liste von Vor- und Nachteilen enthält.

  

Eine weitere Sache, die ich nicht verstehe, ist der Laufzeit-Konstanten-Pool. Es sollte "eine pro-Klasse- oder pro-Schnittstelle-Laufzeitdarstellung der Tabelle" constant_pool "in einer Klassendatei sein", aber ich glaube nicht, dass ich verstehe, was es tut.

Überlegen Sie:

%Vor%

s, i und j sind alle Variablen und können jeweils zu einem späteren Zeitpunkt im Programm geändert werden. "Hello World" ist jedoch ein Objekt vom Typ String, das nicht geändert werden kann, 5 ist ein int, das nicht geändert werden kann, und "Hello World" .length () kann zur Kompilierungszeit immer 11 zurückgegeben werden. Diese Konstanten sind Es können gültige Objekte und Methoden aufgerufen werden (zumindest auf der Zeichenkette), so dass sie irgendwo zugewiesen werden müssen. Aber sie können niemals geändert werden. Wenn diese Konstanten zu einer Klasse gehören, werden sie in einem Konstantenpool pro Klasse zugeordnet. Andere konstante Daten, die nicht Teil einer Klasse sind (wie die ID des main () -Threads) werden in dem per-runtime-Konstanten-Pool zugeordnet ("runtime" bedeutet in diesem Fall "Instanz der JVM").

Der C ++ - Standard hat eine Sprache über eine ähnliche Technik, aber die Implementierung bleibt dem Binärformat (ELF, a.out, COFF, PE usw.) überlassen. Der Standard erwartet, dass Konstanten mit ganzzahligen Datentypen (bool, int, long usw.) oder c-style Strings tatsächlich in einem konstanten Teil des Binary gehalten werden, während andere konstante Daten (doubles, floats, classes) gespeichert werden können als eine Variable zusammen mit einem Flag, das besagt, dass die "Variable" nicht modifizierbar ist (es ist auch akzeptabel, sie mit Integral- und C-Style-String-Konstanten zu speichern, aber viele Binärformate machen dies nicht zu einer Option).

Im Allgemeinen kann der "konstante Datenabschnitt" einer Binärdatei geteilt werden, wenn mehr als eine Kopie eines Programms gleichzeitig geöffnet ist (weil konstante Daten in jeder Kopie des Programms identisch sind). Auf ELF wird dieser Abschnitt als .rodata-Abschnitt bezeichnet.

    
Max Lybbert 15.05.2009 07:57
quelle
3

Was genau war deine Aufgabe?

Der Hauptunterschied zwischen Java und C ++ besteht darin, dass Java von der VM als Garbage Collection erfasst wird, während in C ++ das Programm direkt auf der Maschine ausgeführt wird und der Speicher über OS-Dienste verwaltet wird.

Bezüglich des Stacks ist ein Frame nur eine "offizielle" und Standardform von C ++ - Compilern. C ++ - Compiler legen die Dinge im Stapel einfach übereinander, während Sie von Anruf zu Anruf wechseln. In Java ist der Begriff Frame, und da kompilierter Java-Code auf jeder Plattform ausgeführt werden sollte, gibt es sehr klare Standards, wie dies geschieht. In C ++ kann jeder Compiler den Stapel unterschiedlich behandeln (z. B. sogar durch die Art der Wortgröße).

In Java läuft alles innerhalb der VM, die alles verwaltet, obwohl es einiges an die Umgebung delegiert. Mit anderen Worten, Sie haben keinen Zugriff darauf, wo die JVM Ihre Daten und Ihren Code eingibt, und Ihr Code wird möglicherweise niemals zu einem echten "Code-Segment". Mit anderen Worten, dies kann nicht wirklich beantwortet werden. In C ++ funktioniert alles auf der Hardware, also haben Sie Stapelsegmente, Datensegmente usw. Suchen Sie Informationen über C ++.

In C ++ haben Klassen zur Laufzeit keine Repräsentation im Speicher; Tatsächlich können Sie C ++ in C kompilieren und dann die Ergebnisse in Assembly kompilieren. In Java wird zur Laufzeit auch alles dargestellt, so dass Sie ein Objekt fragen können, welcher Klasse es angehört und welche Methode unterstützt wird. Daher hat jede Klassendatei einen "Konstantenpool", in dem die Zeichenfolgen angezeigt werden, die solche Dinge wie Methodennamen, Feldnamen usw. darstellen. Die eigentliche Klassendefinition bezieht sich auf den Pool. Mit anderen Worten, das hat sehr wenig mit Stack-Frames zu tun. In Stack-Frames werden Methodenparameter, lokale Variablen und Rückgabewerte gespeichert.

    
Uri 14.01.2009 00:17
quelle

Tags und Links