Haufen und Speicheroptimierung
Heute habe ich die automatische Datensortierungsfunktion eines Projekts getestet und zehn Tausende von Datensätzen und Bildern in der Datenbank aussortiert. Als die Operation kurz vor dem Ende war, wurde Java.lang.outofMemoryError ein Fehler im Java Heap -Raum enthüllt. In der Vergangenheit habe ich solche Speicherfehler bei Schreiben von Programmen selten begegnet, da Java einen Müllsammlermechanismus hat, sodass ich ihm nicht viel Aufmerksamkeit geschenkt habe. Heute habe ich online nach Informationen gesucht und sie auf dieser Grundlage aussortiert.
1. Stapel und Stapel
Haufen mit neuem Müllsammler ist für das Recycling verantwortlich
1. Wenn das Programm ausgeführt wird, erhält der JVM einen Speicher aus dem Betriebssystem, dessen Teil der Heap -Speicher ist. Der Heap -Speicher ist normalerweise am Ende der Speicheradresse nach oben angeordnet.
2. Der Haufen ist ein "Laufzeit" -Datenbereich, und das in der Klasse instanziierte Objekt verteilt Raum aus dem Haufen.
3. Die Zuordnung von Raum auf dem Haufen wird durch Anweisungen wie "neu" festgelegt. Der Haufen ist die dynamisch zugewiesene Speichergröße, und die Lebensdauer muss dem Compiler nicht im Voraus mitgeteilt werden.
4. Im Gegensatz zu C ++ verwaltet Java den Haufen und den Stapel automatisch, und der Müllkollektor kann den nicht mehr verwendeten Heap -Speicher automatisch recyceln.
5. Der Nachteil ist, dass der Speicherzugriffsgeschwindigkeit, da der Speicher zur Laufzeit dynamisch zugewiesen wird, langsamer ist.
Stack - Grundlegende Typen und Referenztypen speichern, schnell
1. Die Erst- und anschließende Datenstruktur wird normalerweise verwendet, um Parameter und lokale Variablen in der Methode zu speichern.
2. In Java werden alle Variablen grundlegender Typen (kurz, int, lang, byte, float, doppelt, boolean, char) und Referenztypen im Stapel gespeichert;
3. Der Wohnraum der Daten im Stapel befindet sich im Allgemeinen in den aktuellen Tischblättern (der Bereich, der von {...} eingeschlossen ist;
V.
5. Die Daten im Stapel können gemeinsam genutzt werden, und mehrere Referenzen können auf dieselbe Adresse hinweisen.
6. Der Nachteil ist, dass die Datengröße und Lebensdauer des Stapels bestimmt werden müssen und die Flexibilität mangelt.
2. Speichereinstellungen
1. Überprüfen Sie den Speicher des virtuellen Maschinenspeichers
Long maxcontrol = runTime.getRuntime (). maxMemory (); // Erhalten Sie die maximale Menge an Speicher, die die virtuelle Maschine long Currentuse steuern kann
Standardmäßig maxControl = 66650112b = 63,5625 m der virtuellen Java -Maschine;
Wenn Sie nichts tun, ist die auf meiner Maschine gemessene Stromuse = 5177344b = 4,9375 m;
2. Befehl zum Festlegen der Speichergröße
-Xms <größe> Setzen Sie die anfängliche Java -Heap -Größe: Setzen Sie die JVM -Initialisierungshaufenspeichergröße; Dieser Wert kann genauso wie -xmx festgelegt werden, um das JVM -Umverteilungspeicher bei jedem Abschluss der Müllsammlung zu vermeiden.
-Xmx <größe> Maximale Java -Heap -Größe einstellen: Setzen Sie die maximale Heap -Speichergröße des JVM;
-Xmn <größe>: Setzen Sie die Größe der jungen Generation, die gesamte Haufengröße = die Größe der jungen Generation + die Größe der alten Generation + die Größe der letzten Generation.
-XSS <Size> Set Java -Thread -Stapelgröße: Setzen Sie die Speichergröße des JVM -Thread -Stapels;
3. Spezifische Operationen (1) JVM -Speichereinstellungen:
Offen
Eingeben: -xmx128m -xms64m -xmn32m -xss16m
(2) IDE -Speichereinstellungen:
Ändern Sie die Konfiguration unter -vmargs in myeClipse.ini (oder eclipse.ini im Eclipse -Root -Verzeichnis):
(3) Tomcat -Speichereinstellungen
Öffnen Sie den Bin -Ordner im Stammverzeichnis von Tomcat und bearbeiten Sie Catalina.bat
Ändern Sie: Java_Opts = -xms256m -xmx512m festlegen
3.. OutofMemoryError -Fehleranalyse im Java -Haufen
Wenn der JVM gestartet wird, wird der vom Parameter -xms festgelegte Heap -Speicher verwendet. Während das Programm weitergeht und mehr Objekte erstellt, beginnt das JVM, den Heap -Speicher zu erweitern, um mehr Objekte zu halten. Das JVM verwendet auch einen Müllsammler, um den Speicher zu recyceln. Wenn der von -xmx festgelegte maximale Heap -Speicher fast erreicht ist und dem neuen Objekt kein Speicher mehr zugeordnet werden kann, wird der JVM Java.lang.outofMemoryError werfen und das Programm stürzt ab. Bevor Sie das Ausschalten vonMemoryError haben, wird der JVM versuchen, mit dem Müllsammler genug Platz freizugeben, aber diesen Fehler werfen, wenn er feststellt, dass immer noch nicht genügend Platz vorhanden ist. Um dieses Problem zu lösen, müssen Sie sich über die Informationen zu den Programmobjekten klar machen, z. "Java.lang.outofMemoryError: Java Heap Space" bedeutet, dass der Haufen nicht genügend Platz hat und nicht weiter expandieren kann. "java.lang.outofMemoryError: Permgen Space" bedeutet, dass die dauerhafte Generation voll ist und Ihr Programm die Klasse nicht mehr laden oder eine Zeichenfolge zuordnen kann.
4. Haufen und Müllsammlung
Wir wissen, dass Objekte im Heap -Speicher erstellt werden. Die Müllsammlung ist ein Prozess, der tote Objekte aus dem Haufen von Haufen löscht und diesen Speicher an den Haufen zurückgibt. Um den Müllsammler zu verwenden, ist der Haufen hauptsächlich in drei Bereiche unterteilt, nämlich der neuen Generation, der alten Generation oder der ansässigen Generation und des Perm -Raums. Neue Generation ist ein Raum, mit dem neu erstellte Objekte gespeichert werden können, und wird verwendet, wenn das Objekt neu erstellt wird. Bei langer Zeit werden sie vom Garbage Collector zur alten Generation (oder der festen Generation) verlegt. In Perm Space speichert der JVM Meta-Daten wie Klassen, Methoden, Stringpools und Details auf Klassenebene.
5. Zusammenfassung:
1. Java Heap -Speicher ist Teil des Speicherspeichers, der dem Betriebssystem dem JVM zugewiesen wird.
2. Wenn wir Objekte erstellen, werden sie im Java Heap -Speicher gespeichert.
3. Um die Müllsammlung zu erleichtern, ist der Java Heap -Raum in drei Gebiete unterteilt, die als New Generation, Old Generation oder Seenured Generation und Perm Space bezeichnet werden.
4.. Sie können die Größe des Java -Heap -Speicherplatzes anpassen, indem Sie die JVM -Befehlszeilenoptionen -xms, -xmx und -xmn verwenden.
5. Sie können JConsole oder Runtime.maxMemory (), Runtime.totalMemory () und Runtime.Freememory () verwenden, um die Größe des Heap -Speichers in Java anzuzeigen.
6. Sie können den Befehl "JMAP" verwenden, um den Heap -Dump zu erhalten, und "Jhat" zur Analyse des Heap -Dumps verwenden.
7. Java Heap Space unterscheidet sich vom Stapelraum. Der Stack Space wird verwendet, um Anrufstacks und lokale Variablen zu speichern.
8. Der Java -Müllkollektor wird verwendet, um die von toten Objekten (nicht mehr verwendete Objekte, die nicht mehr verwendet werden) und in den Java -Heap -Raum zurückzugewinnen.
9. Wenn Sie auf java.lang.outofMemoryError stoßen, müssen Sie sich keine Sorgen machen. Manchmal muss man nur den Haufen Raum erhöhen. Wenn es jedoch häufig vorkommt, müssen Sie sehen, ob im Java -Programm ein Speicherleck vorhanden ist.
10. Verwenden Sie Profiler- und Heap -Dump -Analyse -Tools, um den Java -Heap -Speicherplatz anzuzeigen, und Sie können sehen, wie viel Speicher für jedes Objekt zugewiesen wird.
Detaillierte Erklärung des Stapelspeichers
Java Stack Storage hat die folgenden Eigenschaften:
1. Die Datengröße und der Lebenszyklus im Stapel müssen bestimmt werden.
Zum Beispiel die Speicherung des Grundtyps: int a = 1; Diese Variable enthält einen wörtlichen Wert, A ist ein Verweis auf den Int -Typ, der auf den wörtlichen Wert von 3. hinweist. Aufgrund der Größe und Lebensdauer dieser wörtlichen Daten werden diese wörtlichen Werte in einem Programmblock fest definiert, und nach dem Ausflug des Programmblocks verschwinden die wörtlichen Werte im Stapel, um die Geschwindigkeit zu verfolgen.
2. Die im Stapel vorhandenen Daten können gemeinsam genutzt werden.
(1) Grundlegende Datenspeicherung:
wie:
int a = 3; int b = 3;
Der Compiler verarbeitet zuerst int a = 3; Zuerst wird ein Verweis auf die Variable A im Stapel erzeugt und dann herausfinden, ob es eine Adresse mit einem wörtlichen Wert von 3 gibt. Wenn sie nicht gefunden wird, wird eine Adresse mit dem wörtlichen Wert von 3 geöffnet und dann auf die Adresse von 3 an die Adresse 3 verarbeiten. Int B = 3; Nachdem die Referenzvariable von B erstellt wurde, wird B im Stapel bereits ein wörtlicher Wert von 3 vorhanden, sondern auf diese Weise direkt auf die Adresse von 3 hingewiesen. A und B zeigen beide gleichzeitig auf 3.
Hinweis: Diese wörtliche Referenz unterscheidet sich von der von Klassenobjekten. Unter der Annahme, dass die Referenzen von zwei Klassenobjekten gleichzeitig auf ein Objekt verweisen, spiegelt die andere Objektreferenzvariable diese Änderung sofort wider. Stattdessen führt die Änderung des Wertes durch eine wörtliche Referenz nicht zu einem entsprechenden Wert, dass ein weiterer Wert geändert wird. Wie im obigen Beispiel, nachdem wir die Werte von A und B definiert haben, sei a = 4; Dann ist B nicht gleich 4 oder in einem Inneren des Compilers, wenn A = 4 angetroffen wird, wird es erneut suchen, ob im Stapel einen wörtlichen Wert von 4 vorhanden ist. Wenn nicht, öffnen Sie die Adresse erneut, um den Wert von 4 zu speichern. Wenn es bereits vorhanden ist, zeigen Sie direkt A auf diese Adresse. Daher wirkt sich die Wertänderung A nicht auf den Wert b aus.
(2) Verpackungsdatenspeicher:
Klassen, die die entsprechenden grundlegenden Datentypen wie Ganzzahl, Doppel, String usw. einwickeln. Alle diese Klassendaten sind im Haufen vorhanden. Java verwendet die neue () Anweisung, um den Compiler anzuzeigen, und erstellt nur nach Bedarf dynamisch zur Laufzeit. Daher ist er flexibler, aber der Nachteil ist, dass es mehr Zeit in Anspruch nimmt.
Zum Beispiel: Nehmen Sie als Beispiel String.
String sind spezielle Verpackungsdaten. Das heißt, es kann in Form von String str = new String ("ABC") erstellt werden. oder es kann in Form von String str = "ABC" erstellt werden. Ersteres ist der standardisierte Erstellungsprozess der Klasse, dh in Java, alles ist ein Objekt, und ein Objekt ist eine Instanz der Klasse, die alle in Form von New () erstellt wurden. Einige Klassen in Java, wie z. Eigentlich ist es nicht der Fall. Diese Klasse verwendet das Singleton -Muster, um eine Instanz der Klasse zurückzugeben. Diese Instanz wird jedoch in der Klasse über New () erstellt, wodurch dieses Detail von außen verbirgt.
Warum wird die Instanz dann nicht über New () in String str = "ABC" erstellt;? Wird es gegen das obige Prinzip verletzt? Eigentlich gibt es nicht.
Über die interne Arbeit von String str = "ABC". Java verwandelt diese Aussage intern in die folgenden Schritte:
A. Definieren Sie zunächst eine Objektreferenzvariable mit dem Namen STR in der String -Klasse: String str;
B. Finden Sie heraus, ob es eine Adresse mit dem Wert "ABC" im Stapel gibt. Wenn nicht, öffnen Sie eine Adresse mit dem wörtlichen Wert "ABC", erstellen Sie ein neues Objekt O der Zeichenfolgeklasse und richten Sie den Zeichenfolgewert von O auf diese Adresse und beachten Sie das Referenzobjekt O neben dieser Adresse im Stapel. Wenn es eine Adresse mit dem Wert "ABC" gibt, suchen Sie nach Objekt O und geben Sie die Adresse von O zurück.
C. Punkt STR auf die Adresse des Objekts O.
Es ist erwähnenswert, dass normalerweise die Stringwerte in der String -Klasse direkt gespeichert werden. In Situationen wie String Str = "ABC";, sein String -Wert enthält einen Verweis auf die im Stapel vorhandenen Daten (d. H. String Str = "ABC"; sowohl Stapelspeicher als auch Haufenspeicher).
Um dieses Problem besser zu veranschaulichen, können wir es durch die folgenden Codes überprüfen.
String str1 = "ABC"; String str2 = "ABC"; System.out.println (str1 == str2); //WAHR
(Der Wahrheitswert wird nur zurückgegeben, wenn beide Referenzen auf dasselbe Objekt verweisen. Werden Str1 und Str2 auf dasselbe Objekt zeigen)
Das Ergebnis zeigt, dass die JVM zwei Referenzen Str1 und Str2 erstellt hat, aber nur ein Objekt wurde erstellt, und beide Referenzen wiesen auf dieses Objekt hin.
String str1 = "ABC"; String str2 = "ABC"; str1 = "bcd"; System.out.println (str1 + "," + str2); // BCD, ABC System.out.println (str1 == str2); //FALSCH
Dies bedeutet, dass die Änderung der Zuordnung zur Änderung der Referenz des Klassenobjekts führt, Str1 auf ein anderes neues Objekt zeigt, während Str2 immer noch auf das ursprüngliche Objekt zeigt. Wenn wir im obigen Beispiel den Wert von STR1 in "BCD" ändern, stellte die JVM fest, dass es keine Adresse gibt, um den Wert im Stapel zu speichern. Daher öffnete sie diese Adresse und erstellte ein neues Objekt, dessen Zeichenfolge Wert auf diese Adresse zeigt.
Tatsächlich ist die String -Klasse als unveränderliche Klasse ausgelegt. Wenn Sie seinen Wert ändern möchten, können Sie, aber das JVM erstellt leise ein neues Objekt, das auf dem neuen Wert zur Laufzeit basiert (es kann nicht basierend auf dem ursprünglichen Speicher geändert werden) und gibt dann die Adresse dieses Objekts an die Referenz der ursprünglichen Klasse zurück. Obwohl dieser Erstellungsprozess vollständig automatisch ist, braucht er doch mehr Zeit. In einer Umgebung, die den Zeitanforderungen empfindlicher ist, hat es bestimmte nachteilige Auswirkungen.
String str1 = "ABC"; String str2 = "ABC"; str1 = "bcd"; String str3 = str1; System.out.println (STR3); // BCD String str4 = "BCD"; System.out.println (str1 == str4); //WAHR
Die Referenz auf STR3 -Objekt zeigt direkt auf das von Str1 hingewiesene Objekt (beachten Sie, dass Str3 kein neues Objekt erstellt). Nachdem Str1 seinen Wert geändert hat, erstellen Sie eine String -Referenz Str4 und verweisen Sie auf ein neues Objekt, das durch STR1 erstellt wurde und den Wert ändert. Es ist festzustellen, dass Str4 diesmal kein neues Objekt erstellt hat und so die Freigabe von Daten im Stapel wieder erkannte.
String str1 = new String ("ABC"); String str2 = "ABC"; System.out.println (str1 == str2); //FALSCHEs wurden zwei Referenzen erstellt. Es wurden zwei Objekte erstellt. Die beiden Referenzen verweisen auf zwei verschiedene Objekte.
String str1 = "ABC"; String str2 = new String ("ABC"); System.out.println (str1 == str2); //FALSCHEs wurden zwei Referenzen erstellt. Es wurden zwei Objekte erstellt. Die beiden Referenzen verweisen auf zwei verschiedene Objekte.
Die beiden oben genannten Codes zeigen, dass das Objekt mit New () im Haufen erstellt und seine Saiten separat gespeichert werden. Selbst wenn sie die Daten im Stapel entsprechen, werden sie nicht mit den Daten im Stapel geteilt.
Zusammenfassen:
(1) Wenn wir eine Klasse unter Verwendung eines Formats wie String str = "ABC" definieren, halten wir es immer als selbstverständlich, dass wir ein Objekt Strs der String -Klasse erstellt haben. Sorgen Sie sich um die Falle! Das Objekt wurde möglicherweise nicht erstellt! Das einzige, was sicher ist, ist, dass ein Hinweis auf die String -Klasse erstellt wird. Was diese Bezugnahme auf ein neues Objekt hinweist, muss sie basierend auf dem Kontext berücksichtigt werden, es sei denn, Sie verwenden die neue () -Methode, um explizit ein neues Objekt zu erstellen. Um genauer zu sein, erstellen wir daher eine Referenzvariable -STR für ein Objekt der String -Klasse, die auf eine String -Klasse mit einem Wert von "ABC" verweist. Das bewusst zu sein, ist hilfreich bei der Fehlerbehebung bei schwierigen Fehlern in Programmen.
(2) unter Verwendung von String str = "ABC"; Kann die Laufgeschwindigkeit des Programms in gewissem Maße verbessern, da die JVM automatisch entscheidet, ob es erforderlich ist, ein neues Objekt basierend auf der tatsächlichen Situation der Daten im Stapel zu erstellen. Für den Code von String Str = New String ("ABC"), werden im Heap neue Objekte erstellt, unabhängig davon, ob ihre Stringwerte gleich sind oder nicht, ob es erforderlich ist, neue Objekte zu erstellen, wodurch die Belastung des Programms erhöht wird.
(3) aufgrund der unveränderlichen Natur der String -Klasse (da der Wert der Wrapper -Klasse nicht geändert werden kann), sollte die StringBuffer -Klasse in Betracht gezogen werden, um die Programmeffizienz zu verbessern.