Was Sie zuerst wissen müssen
1. C/C++-Programmierer verwalten den Speicher selbst, während Java-Speicher automatisch vom GC zurückgefordert wird.
Obwohl ich mit C++ nicht sehr vertraut bin, habe ich dabei wahrscheinlich keinen gesunden Menschenverstandsfehler gemacht.
2. Was ist ein Speicherleck?
Ein Speicherverlust bezieht sich auf das Vorhandensein von Speicher im System, der nicht recycelt werden kann, was manchmal zu unzureichendem Speicher oder einem Systemabsturz führt.
In C/C++ treten Speicherlecks auf, wenn zugewiesener Speicher nicht freigegeben wird.
3. Es gibt einen Speicherverlust in Java. Wir müssen dies zunächst zugeben, bevor wir weiter darüber diskutieren können. Obwohl es in Java Speicherlecks gibt, müssen Sie sich darüber im Grunde keine Gedanken machen, insbesondere für diejenigen, die sich nicht besonders mit dem Code selbst auskennen.
Speicherlecks in Java bedeuten sicherlich: Es gibt nutzlose Objekte, die vom Garbage Collector nicht recycelt werden können.
Und selbst wenn ein Speicherverlustproblem vorliegt, wird es möglicherweise nicht angezeigt.
4. Parameter in Java werden alle als Wert übergeben.
Gegen Basistypen gibt es grundsätzlich keine Einwände, gegen Referenztypen können wir jedoch keine Einwände haben.
Java-Speicherlecks
1. Heap-Speicherüberlauf (outOfMemoryError: Java-Heap-Speicherplatz)
In der JVM-Spezifikation wird der Speicher im Heap zum Generieren von Objektinstanzen und Arrays verwendet.
Bei einer Unterteilung kann der Heap-Speicher auch in die junge Generation und die alte Generation unterteilt werden. Die junge Generation umfasst einen Eden-Bereich und zwei Survivor-Bereiche.
Wenn ein neues Objekt generiert wird, ist der Speicheranwendungsprozess wie folgt:
a. Der JVM versucht zunächst, den für das neue Objekt erforderlichen Speicher im Eden-Bereich zuzuweisen.
b. Wenn die Speichergröße ausreicht, wird die Anwendung beendet, andernfalls ist der nächste Schritt;
c. JVM startet youngGC und versucht, inaktive Objekte im Eden-Bereich freizugeben. Wenn der Eden-Bereich nach der Freigabe immer noch nicht ausreicht, um neue Objekte zu platzieren, versucht es, einige der aktiven Objekte im Eden-Bereich zu platzieren.
d. Der Survivor-Bereich wird als Zwischenaustauschbereich zwischen Eden und Old verwendet. Wenn im OLD-Bereich genügend Platz vorhanden ist, werden die Objekte im Survivor-Bereich in den Old-Bereich verschoben.
e. Wenn im ALTEN Bereich nicht genügend Platz vorhanden ist, führt die JVM eine vollständige GC im ALTEN Bereich durch.
f. Wenn die Survivor- und OLD-Bereiche nach der vollständigen GC einige aus Eden kopierte Objekte immer noch nicht speichern können, was dazu führt, dass die JVM keinen Speicherbereich für neue Objekte im Eden-Bereich erstellen kann, wird ein „Nicht genügend Speicher“-Fehler angezeigt:
outOfMemoryError: Java-Heap-Speicherplatz
2. Speicherüberlauf im Methodenbereich (outOfMemoryError: permgem space)
In der JVM-Spezifikation speichert der Methodenbereich hauptsächlich Klasseninformationen, Konstanten, statische Variablen usw.
Wenn das Programm zu viele Klassen lädt oder dynamische Proxy-Generierungstechnologien wie Reflection oder gclib verwendet, kann es daher zu einem Speicherüberlauf in diesem Bereich kommen. Im Allgemeinen lautet die Fehlermeldung, wenn in diesem Bereich ein Speicherüberlauf auftritt:
outOfMemoryError: Permgem-Speicherplatz
3. Thread-Stack-Überlauf (java.lang.StackOverflowError)
Der Thread-Stapel ist eine für den Thread einzigartige Speicherstruktur. Daher müssen Probleme mit dem Thread-Stapel Fehler sein, die beim Ausführen eines Threads generiert werden.
Im Allgemeinen wird ein Thread-Stack-Überlauf durch eine zu tiefe Rekursion oder zu viele Ebenen von Methodenaufrufen verursacht.
Die Fehlermeldung, wenn ein Stapelüberlauf auftritt, lautet:
Java. lang. StackOverflowError
Mehrere Szenarien für Speicherlecks:
1. Langlebige Objekte enthalten Verweise auf kurzlebige Objekte.
Dies ist das häufigste Szenario für Speicherlecks und ein häufiges Problem beim Codedesign.
Beispiel: Wenn lokale Variablen in einer globalen statischen Karte zwischengespeichert werden und kein Löschvorgang erfolgt, wird die Karte mit der Zeit immer größer, was zu Speicherverlusten führt.
2. Ändern Sie den Parameterwert des Objekts im Hashset. Der Parameter ist das Feld, das zur Berechnung des Hashwerts verwendet wird.
Nachdem ein Objekt in der HashSet-Sammlung gespeichert wurde, können die Felder im Objekt, die an der Berechnung des Hash-Werts beteiligt sind, nicht mehr geändert werden. Andernfalls unterscheidet sich der geänderte Hash-Wert des Objekts vom Hash-Wert, als er ursprünglich in der HashSet-Sammlung gespeichert wurde . In diesem Fall gibt die Methode „Contains“ auch dann das Ergebnis zurück, dass das Objekt nicht gefunden werden kann, wenn sie die aktuelle Referenz des Objekts als Parameter zum Abrufen des Objekts aus der HashSet-Sammlung verwendet, was ebenfalls zu einem Fehler führt Löschen Sie das aktuelle Objekt aus der HashSet-Sammlung, was zu einem Speicherverlust führt.
3. Stellen Sie die Anzahl der Verbindungen und die Abschaltzeit der Maschine ein
Auch das Öffnen einer sehr ressourcenintensiven Verbindung über einen längeren Zeitraum kann zu Speicherverlusten führen.
Schauen wir uns ein Beispiel für einen Speicherverlust an:
public class Stack { private Object[] elements=new Object[10]; private int size = 0; public void push(Object e){ activateCapacity(); public Object pop(){ if( size == 0) throw new EmptyStackException(); return elements[--size]; } private void secureCapacity(){ if(elements.length == size){ Object[] oldElements = Elemente; Elemente = neues Objekt[2 * Elemente. Länge+1]; System. arraycopy(oldElements,0, elements, 0, size);
Das obige Prinzip sollte sehr einfach sein. Wenn 10 Elemente zum Stapel hinzugefügt werden und dann alle herausspringen, kann dieses Objekt nicht recycelt werden, obwohl der Stapel leer ist. Dies erfüllt die beiden Anforderungen von Speicherlecks. Zustand: Unbrauchbar, nicht recycelbar.
Aber selbst die Existenz eines solchen Dings muss nicht zwangsläufig Konsequenzen haben, wenn dieser Stapel weniger verwendet wird.
Es ist nur eine Verschwendung von ein paar K Speicher. Wie auch immer, unser Speicher ist bereits auf G, also welche Auswirkungen wird es haben? Außerdem wird dieses Ding bald recycelt, also was spielt es für eine Rolle? Schauen wir uns unten zwei Beispiele an.
Beispiel 1
öffentliche Klasse Bad{ public static Stack s=Stack(); push(new Object()); s. pop(); //Hier liegt ein Speicherverlust in einem Objekt vor. push(new Object()); //Das obige Objekt kann recycelt werden, was einer Selbstheilung entspricht}}
Da es statisch ist, bleibt es bestehen, bis das Programm beendet wird. Wir können jedoch auch erkennen, dass es über eine Selbstheilungsfunktion verfügt.
Das heißt, wenn Ihr Stack höchstens 100 Objekte enthält, können höchstens 100 Objekte nicht recycelt werden. Tatsächlich sollte dies intern leicht zu verstehen sein. Im schlimmsten Fall sind sie alle nutzlos , denn Sobald wir neue Fortschritte erzielen, verschwinden die vorherigen Referenzen natürlich!
Beispiel 2
öffentliche Klasse NotTooBad{ public void doSomething(){ Stack s=new Stack( s); push(new Object()); //anderer Code s. pop();//Dies führt auch dazu, dass das Objekt nicht recycelt werden kann und Speicher verloren geht. }// Beenden Sie die Methode, s ist automatisch ungültig, s kann recycelt werden und die Referenzen im Stapel sind natürlich verschwunden, sodass // hier auch eine Selbstheilung durchgeführt werden kann, und man kann sagen, dass diese Methode nicht vorhanden ist Ein Speicherverlustproblem, aber es wird später eingereicht. // Es wird nur an GC weitergegeben, da es geschlossen und nicht für die Außenwelt offen ist. Sie können das oben Gesagte sagen Code 99. In 9999 % aller Situationen hat das Schreiben eines solchen Codes keine negativen Auswirkungen, aber // es handelt sich definitiv um Müllcode. Ich werde eine hinzufügen. Eine leere for-Schleife wird keine große Auswirkung haben, oder?
Die beiden oben genannten Beispiele sind nur trivial, aber Speicherlecks in C/C++ sind nicht schlimm, sondern am schlimmsten.
Wenn sie nicht an einem Ort recycelt werden, können sie niemals recycelt werden. Wenn Sie diese Methode häufig aufrufen, wird der Speicher aufgebraucht!
Da Java auch über eine Selbstheilungsfunktion verfügt (ich habe sie selbst benannt und noch kein Patent angemeldet), kann das Speicherleckproblem von Java fast ignoriert werden, aber wer es kennt, sollte es nicht begehen.
Um Speicherlecks zu vermeiden, können Sie beim Schreiben von Code auf die folgenden Vorschläge zurückgreifen:
1. Verweise auf nutzlose Objekte so früh wie möglich freigeben;
2. Verwenden Sie die String-Verarbeitung, vermeiden Sie die Verwendung von String und verwenden Sie StringBuffer ausgiebig. Jedes String-Objekt muss einen unabhängigen Speicherbereich belegen.
3. Verwenden Sie so wenig statische Variablen wie möglich, da statische Variablen in der permanenten Generation (Methodenbereich) gespeichert werden und die permanente Generation grundsätzlich nicht an der Speicherbereinigung teilnimmt.
4. Vermeiden Sie es, Objekte in Schleifen zu erstellen;
5. Das Öffnen großer Dateien oder das gleichzeitige Entnehmen zu vieler Daten aus der Datenbank kann leicht zu einem Speicherüberlauf führen. Daher sollten Sie an diesen Stellen die maximale Datenmenge grob berechnen und die erforderlichen minimalen und maximalen Speicherplatzwerte festlegen.