Seit der Arbeit wurde immer mehr Code geschrieben, das Programm ist immer mehr aufgebläht und die Effizienz ist immer weniger geworden. Dies ist für einen Programmierer wie mich, der Perfektion verfolgt, absolut nicht erlaubt. Zusätzlich zur ständigen Optimierung der Programmstruktur sind die Speicheroptimierung und die Leistungseinstellung zu meinen üblichen "Tricks" geworden.
Um Java -Programme Speicher und Leistung zu optimieren und abzustimmen, ist es definitiv nicht möglich, die internen Prinzipien virtueller Maschinen (oder strengere Spezifikationen) nicht zu verstehen. Hier ist ein gutes Buch "Eingehende Java Virtual Machine (Second Edition)" (von Bill Venners, übersetzt von Cao Xiaogang und Jiang Jing. Tatsächlich ist dieser Artikel das persönliche Verständnis des Autors für Java Virtual Machines nach dem Lesen dieses Buches). Natürlich sind die Vorteile des Verständnisses von Java -virtuellen Maschinen nicht auf die beiden Vorteile beschränkt. Aus technischer Sicht wird das Verständnis der Spezifikationen und der Implementierung von Java -Virtual -Maschinen für uns hilfreicher sein, um einen effizienten und stabilen Java -Code zu schreiben. Wenn wir beispielsweise das Speichermodell der java -virtuellen Maschine und den Speicherrecycling -Mechanismus der virtuellen Maschine verstehen, werden wir uns nicht zu sehr darauf verlassen, sondern werden ausdrücklich den Speicher "Speicher" verweisen, wenn erforderlich (Java -Code kann nicht explizit den Speicher freigeben, aber wir können den Müllsammler darüber informieren, dass der Objekt den Verbrauch von unnötigem Erinnern reduziert werden muss). Wenn wir verstehen, wie der Java -Stack funktioniert, können wir das Risiko eines Stapelüberlaufs verringern, indem wir die Anzahl der rekursiven Schichten und die Anzahl der Schleifen verringern. Für Anwendungsentwickler können sie möglicherweise nicht direkt die Arbeit der zugrunde liegenden Implementierung dieser java -virtuellen Maschinen beinhalten, aber das Verständnis dieses Hintergrundwissens hat mehr oder weniger subtile und gute Auswirkungen auf die von uns geschriebenen Programme.
In diesem Artikel wird kurz das Architektur und das Speichermodell der virtuellen Java -Maschine erläutert. Wenn es unangemessene Wörter oder ungenaue Erklärungen gibt, stellen Sie bitte sicher, dass Sie sie korrigieren. Ich fühle mich sehr geehrt!
Java Virtual Machine Architektur
Klassenbeladungssubsystem
Es gibt zwei Klassenlader für Java Virtual Machines, nämlich den Start-Class-Loader und den benutzerdefinierten Loader.
Das Laden von Subsystemen der Klasse lädt die Klasse in den Laufzeitbereich über den voll qualifizierten Namen der Klasse (Paketname und Klassenname, Netzwerkmontage auch URL). Für jeden geladenen Typ erstellt die java -virtuelle Maschine eine Instanz der Klasse java.lang.class -Klasse, um den Typ darzustellen, der im Speicherbereich im Speicherbereich platziert ist, und die geladenen Typinformationen befinden sich im Methodenbereich, der wie bei allen anderen Objekten die gleichen entspricht.
Vor dem Laden eines Typs muss das Klassenlade -Subsystem nicht nur die entsprechende Binärklassendatei lokalisieren und importieren, sondern auch die Richtigkeit der importierten Klasse, die Speicher für Klassenvariablen zuzuordnen und initialisieren und Symbolreferenzen als direkte Referenzen analysieren. Diese Aktionen sind ausschließlich in der folgenden Reihenfolge:
1) Laden - Binärdaten vom Typ finden und laden;
2) Verbindung - Überprüfung, Vorbereitung und Parsen durchführen (optional)
3) Überprüfen Sie, ob Sie die Richtigkeit des importierten Typs sicherstellen können
4) Bereiten Sie sich darauf vor, Speicher für Klassenvariablen zuzuweisen und sie in Standardwerte zu initialisieren
5) Analysieren Sie die symbolische Referenz im Typ auf die direkte Anwendung
Methodenbereich
Für jeden vom Klassenlade -Subsystem geladenen Typ speichert die virtuelle Maschine die folgenden Daten in den Methodenbereich:
1. voll qualifizierter Name des Typs
2. Voll qualifizierter Name des Typs Superclass (Java.lang.Object hat keine Superklasse)
3. ist der Typ ein Klassentyp oder ein Schnittstellentyp
4. Geben Sie Zugriffsmodifikator ein
5. Vollqualifizierter Name bestellte Liste eines direkten Hyperinterface
Zusätzlich zu den oben genannten Grundtypinformationen werden auch folgende Informationen gespeichert:
6. Typ konstanter Pool Typ
7. Feldinformationen (einschließlich Feldname, Feldtyp, Feldmodifikator)
8. Methodeninformationen (einschließlich Methodenname, Rückgabetyp, Nummer und Art von Parametern, Methodenmodifikatoren. Wenn die Methode nicht abstrakt und lokal ist, wird die Methode -Bytecode, Operand -Stapel und die Größe und Ausnahmeetabelle des lokalen variablen Bereichs im Method -Stack -Frame auch gespeichert)
9. Alle Klassenvariablen außer Konstanten (eigentlich sind sie statische Variablen der Klasse. Da statische Variablen von allen Instanzen geteilt werden und direkt mit dem Typ zusammenhängen, sind sie Variablen auf Klassenebene und werden im Methodenbereich als Mitglieder der Klasse gespeichert).
10. Ein Hinweis auf den Classloader
// Die zurückgegebene Referenzstring.Class.getClassloader () wurde gerade jetzt gespeichert. Ein Verweis auf die Klassenklasse // wird die Referenzzeichenfolge zurückgeben. Klasse der Klassenklasse, die gerade gerade gespeichert wurde.
Beachten Sie, dass der Methodenbereich auch vom Müllsammler recycelt werden kann.
Haufen
Alle Klasseninstanzen oder Arrays, die von Java-Programmen zur Laufzeit erstellt wurden, werden in denselben Haufen platziert, und jede java-virtuelle Maschine verfügt außerdem über einen Haufen und alle Threads teilen sich einen Haufen (aus diesem Grund verursacht ein Java-Programm mit Multi-Threaded Synchronisierungsproblemen im Objektzugriff).
Da jede java -virtuelle Maschine unterschiedliche Implementierungen der Spezifikation für virtuelle Maschine hat, wissen wir möglicherweise nicht, in welchem Formular jede Java -Virtual -Maschine Objektinstanzen auf dem Haufen darstellt, aber wir können einen Blick auf die folgenden möglichen Implementierungen erhalten:
Programmschalter
Für das Ausführen von Java -Programmen verfügt jeder Thread über ein eigenes PC -Register (Programmzähler), das beim Start des Threads erstellt wird, mit einer Größe eines Wortes, und wird verwendet, um den Standort der nächsten Codezeile zu speichern, die ausgeführt werden muss.
Java Stack
Jeder Thread verfügt über einen Java -Stack, der den Laufstatus des Fadens in Einheiten von Stapelrahmen rettet. Es gibt zwei Arten von Operationen virtueller Maschinen auf dem Java -Stack: Stapelpress und Stapel, die beide Rahmen haben. Der Stack -Frame speichert Daten wie eingehende Parameter, lokale Variablen, Zwischenbetriebsergebnisse usw., die beim Abschluss der Methode aufgetaucht und dann freigegeben werden.
Schauen Sie sich den Speicher -Snapshot des Stapelrahmens an, wenn zwei lokale Variablen addiert werden
Lokaler Methodenstapel
Hier ruft Java die Lokalbibliothek des Betriebssystems auf, die zur Implementierung von JNI (Java Native Interface, Java Local Interface) verwendet wird.
Ausführungsmaschine
Der Kern der virtuellen Java -Maschine steuert das Laden von Java -Bytecode und Parsen; Für das Ausführen von Java -Programmen ist jeder Thread eine Instanz einer unabhängigen virtuellen Maschinenausführungsmaschine. Vom Anfang bis zum Ende des Thread -Lebenszyklus führt es entweder Bytecode aus oder führt lokale Methoden aus.
Lokale Schnittstelle
Mit der lokalen Methodenstapel- und Betriebssystembibliothek verbunden.
HINWEIS: Alle im Artikel genannten Orte Siehe "Java Virtual Machine Spezifikationen für Javaee- und Javase -Plattformen".
Praxis der virtuellen Maschinespeicheroptimierung
Da Speicher erwähnt wird, müssen Speicherlecks erwähnt werden. Wie wir alle wissen, hat Java aus der Grundlage von C ++ entwickelt, und ein großes Problem mit C ++ - Programmen ist, dass Speicherlecks schwer zu lösen sind. Obwohl Javas JVM seinen eigenen Müllsammlungsmechanismus zum Recyceln des Gedächtnisses hat, müssen sich Java -Programmentwickler in vielen Fällen nicht zu sehr Sorgen machen, aber es gibt auch Leckprobleme, die nur etwas kleiner als C ++ sind. Zum Beispiel gibt es im Programm ein referenziertes, aber nutzloses Objekt: Wenn das Programm auf das Objekt verweist, es aber in Zukunft nicht verwenden wird oder nicht, wird der von ihm einbezogene Speicherplatz verschwendet.
Schauen wir uns zunächst an, wie GC funktioniert: Überwachen Sie den laufenden Status jedes Objekts, einschließlich Anwendung, Zitat, Zitat, Zuordnung usw. Wenn das Objekt nicht mehr zitiert wird, geben Sie das Objekt frei (der Fokus von GC Dieser Artikel wird nicht zu viel erklärt). Viele Java -Programmierer verlassen sich zu sehr auf GC, aber der Schlüssel zum Problem ist, dass das Gedächtnis immer eine begrenzte Ressource ist, egal wie gut der Mechanismus des JVM -Mülls für die Müll für die Mülleimer ist. Selbst wenn GC den größten Teil der Müllsammlung für uns abgeschlossen hat, ist es dennoch erforderlich, während des Codierungsprozesses angemessen auf die Speicheroptimierung zu achten. Dies kann die Anzahl der GCs effektiv verringern, gleichzeitig die Speicherauslastung verbessern und die Programmeffizienz maximieren.
Insgesamt sollte die Speicheroptimierung von Java -virtuellen Maschinen von zwei Aspekten beginnen: virtuelle Java -Maschinen und Java -Anwendungen. Ersteres bezieht sich auf die Steuerung der Größe der logischen Speicherpartition der virtuellen Maschine über virtuelle Maschinenparameter entsprechend dem Entwurf der Anwendung, sodass der Speicher des virtuellen Maschine die Speicheranforderungen des Programms ergänzt. Letzteres bezieht sich auf die Optimierung von Programmalgorithmen, die Verringerung der GC -Belastung und die Verbesserung der Erfolgsrate des GC -Recyclings.
Die Parameter zur Optimierung des Speicher des virtuellen Maschinenspeichers durch Parameter sind wie folgt:
XMS
Anfängliche Haufengröße
Xmx
Java Heap Maximalwert
1mn
Haufengröße der jungen Generation
XSS
Stapelgröße für jeden Thread
Die oben genannten sind drei häufig verwendete Parameter, einige:
XX: Minheapfreeratio = 40
Mindestprozentsatz an haufenfrei nach GC, um eine Expansion zu vermeiden.
Xx: maxheapfreeratio = 70
Maximaler Prozentsatz von haufenfreien Nach nach GC, um ein Schrumpfen zu vermeiden.
Xx: newratio = 2
Verhältnis der Größen der neuen/alten Generation. [Sparc -Client: 8; x86 -Server: 8; x86 -Client: 12.] -Client: 8 (1.3.1+), x86: 12]
XX: Newsize = 2,125 m
Standardgröße der neuen Generation (in Bytes) [5.0 und neuer: 64 -Bit -VMs sind 30% größer; x86: 1m; x86, 5,0 und älter: 640k]
Xx: maxnewSize =
Maximale Größe der neuen Generation (in Bytes). Seit 1.4 wird MaxNewSize als Funktion von Newratio berechnet.
Xx: Survivorratio = 25
Verhältnis von Eden/Survivor -Raumgröße [SPARC in 1.3.1: 25; Andere Solaris -Plattformen in 5.0 und früher: 32]
Xx: permSize =
Anfangsgröße der dauerhaften Erzeugung
Xx: maxpermsize = 64m
Größe der dauerhaften Generation. [5.0 und neuer: 64 -Bit -VMs sind 30% größer; 1,4 AMD64: 96 m; 1.3.1 -Client: 32m.]
Was nachstehend erwähnt wird, um die Speicherauslastung zu verbessern und Speicherrisiken durch Optimierung von Programmalgorithmen zu verringern, ist vollständig empirisch und nur als Referenz. Wenn es Unangemessenheit gibt, korrigieren Sie mich bitte, danke!
1. Freie die Referenz nutzloser Objekte so schnell wie möglich frei (xx = null;)
Schauen Sie sich ein Stück Code an:
public list <Paceedata> parse (htmlpage Seite) {list <Paceedata> list = null; try {list valuelist = page.getByXPath (config.GetContentXPath ()); if (valuelist == null || valuelist.isempty ()) {return list; } // Erstellen Sie bei Bedarf ein Objekt, speichern Sie den Speicher und verbessern Sie die Effizienzliste = new ArrayList <Pagedata> (); Pagedata pagedata = new pagedata (); StringBuilder value = new StringBuilder (); für (int i = 0; i <valuelist.size (); i ++) {htmlelement content = (htmlelement) valuelist.get (i); Domnodelist <htmlelement> imgs = content.getElementsByTagName ("img"); if (imgs! String path = image.getSrcAttribute (); String format = path.substring (path.lastIndexof ("."), Path.length ()); String localPath = "d:/bils/" + md5helper.md5 (Pfad) .Replace ("//", ","). Ersetzen ("/", ",") + Format; Datei localFile = new Datei (LocalPath); if (! localfile.exists ()) {localFile.createNewFile (); Image.Saveas (LocalFile); } image.setattribute ("src", "file: ////" + localPath); localFile = null; Bild = null; img = null; } catch (Ausnahme e) {}} // Dieses Objekt wird in Zukunft nicht verwendet. Das Löschen des Verweiss darauf entspricht dem im Voraus mitgeteilten GC. Das Objekt kann IMGS = null recyceln; } String text = content.asxml (); value.Append (text) .Append ("<br/>"); Valuelist = null; content = null; text = null; } pagedata.setContent (value.toString ()); pagedata.setcharset (page.getPageCoding ()); list.add (pagedata); // das pagedata = null; ist nutzlos, da die Liste immer noch den Verweis auf das Objekt enthält und GC den Wert von IT nicht recyceln = null; // Es gibt hier keine Liste = null; Da die Liste der Rückgabewert der Methode ist, ist der Rückgabewert, den Sie aus der Methode erhalten, immer leer, und diese Art von Fehler ist nicht einfach zu entdecken oder ausgeschlossen zu werden} catch (Ausnahme E) {} Rückgabeliste; }2. Verwenden Sie die Sammlungsdatentypen sorgfältig, z. B. Arrays, Bäume, Grafiken, verknüpfte Listen und andere Datenstrukturen. Diese Datenstrukturen sind komplizierter für das Recyceln für GC.
3. Vermeiden Sie explizit den Array -Raum. Wenn Sie explizit beantragen müssen, versuchen Sie, seinen angemessenen Wert so genau wie möglich zu schätzen.
4. Versuchen Sie, eine große Anzahl von Objekten im Standardkonstruktor der Klasse zu erstellen und zu initialisieren, und verhindern Sie unnötige Verschwendung von Speicherressourcen, wenn Sie seinen eigenen Konstruktor der Klasse aufrufen.
5. Versuchen Sie, das erzwungene System zum Recyceln von Müllspeichern zu vermeiden und das letzte Zeitpunkt des Müllrecyclings im System zu erhöhen
6. Versuchen Sie, Instantwertvariablen bei der Entwicklung von Remote -Methoden -Aufrufanwendungen zu verwenden, es sei denn, der Remote -Anrufer muss den Wert der Instantwertvariablen erhalten.
7. Versuchen Sie, die Objektpooling -Technologie in geeigneten Szenarien zu verwenden, um die Systemleistung zu verbessern