Frage Der Umgang mit "java.lang.OutOfMemoryError: PermGen Space" -Fehler


Kürzlich habe ich diesen Fehler in meiner Webanwendung gefunden:

java.lang.OutOfMemoryError: PermGen-Speicherplatz

Es ist eine typische Hibernate / JPA + IceFaces / JSF-Anwendung, die auf Tomcat 6 und JDK 1.6 läuft. Offensichtlich kann dies nach dem erneuten Bereitstellen einer Anwendung einige Male auftreten.

Was verursacht es und was kann getan werden, um es zu vermeiden? Wie behebe ich das Problem?


1178
2017-09-18 03:29


Ursprung


Antworten:


Die Lösung bestand darin, diese Flags zur JVM-Befehlszeile hinzuzufügen, wenn Tomcat gestartet wird:

-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled

Sie können das tun, indem Sie den Tomcat-Dienst herunterfahren, dann in das Tomcat / bin-Verzeichnis gehen und tomcat6w.exe ausführen. Fügen Sie auf der Registerkarte "Java" die Argumente zum Feld "Java-Optionen" hinzu. Klicken Sie auf "OK" und starten Sie den Dienst neu.

Wenn Sie einen Fehler erhalten Der angegebene Dienst existiert nicht als installierter Dienst Du solltest rennen:

tomcat6w //ES//servicename

woher Dienstname ist der Name des Servers, der in services.msc angezeigt wird

Quelle: orxs Kommentar zu Erics Agile Antworten.


549
2017-09-17 22:23



Du versuchst es besser -XX:MaxPermSize=128M eher, als -XX:MaxPermGen=128M.

Ich kann die genaue Verwendung dieses Speicherpools nicht angeben, aber es hängt mit der Anzahl der in die JVM geladenen Klassen zusammen. (Dadurch kann das Entladen der Klasse für tomcat das Problem lösen.) Wenn Ihre Anwendungen während des Laufs Klassen generieren und kompilieren, ist es wahrscheinlicher, dass ein Speicherpool benötigt wird, der größer ist als der Standardwert.


247
2017-09-18 02:09



App Server PermGen-Fehler, die nach mehreren Bereitstellungen auftreten, werden höchstwahrscheinlich durch Referenzen verursacht, die der Container in den Klassenanwendungen Ihrer alten Apps enthält. Wenn Sie beispielsweise eine benutzerdefinierte Protokollstufenklasse verwenden, werden Verweise vom Classloader des Anwendungsservers gehalten. Sie können diese Inter-Classloader-Lecks erkennen, indem Sie moderne (JDK6 +) JVM-Analysetools wie jmap und jhat verwenden, um zu ermitteln, welche Klassen in Ihrer App weiterhin vorhanden sind, und ihre Verwendung neu zu gestalten oder zu eliminieren. Übliche Verdächtige sind Datenbanken, Logger und andere Bibliotheken auf Basis-Framework-Ebene.

Sehen Classloader leckt: die gefürchtete "java.lang.OutOfMemoryError: PermGen space" Ausnahmeund vor allem seine Folgepost.


152
2018-03-11 22:24



Häufige Fehler, die Leute machen, denken, dass Heap-Space und Permgen-Space identisch sind, was überhaupt nicht stimmt. Sie könnten viel Platz auf dem Heap haben, aber immer noch nicht genug Speicher in Permgen.

Häufige Ursachen für OutofMemory in PermGen sind ClassLoader. Wenn eine Klasse in JVM geladen wird, werden alle ihre Metadaten zusammen mit Classloader im PermGen-Bereich gehalten und sie werden als Garbage Collected gesammelt, wenn der Classloader, der sie geladen hat, für die Garbage Collection bereit ist. Wenn der Classloader ein Speicherleck hat, bleiben alle von ihm geladenen Klassen im Speicher und verursachen nach dem wiederholten Wiederholen von permGen outofmemory. Das klassische Beispiel ist Java.lang.OutOfMemoryError: PermGen Space in Tomcat.

Jetzt gibt es zwei Möglichkeiten, dies zu lösen:
1. Finden Sie die Ursache für Memory Leak oder ob ein Speicherleck vorliegt.
2. Erhöhen Sie die Größe von PermGen Space, indem Sie den JVM-Parameter verwenden -XX:MaxPermSize und -XX:PermSize.

Sie können auch überprüfen 2 Lösung von Java.lang.OutOfMemoryError in Java für weitere Details.


65



Verwenden Sie den Befehlszeilenparameter -XX:MaxPermSize=128m für eine Sun JVM (die offensichtlich 128 für jede Größe ersetzt, die Sie benötigen).


39



Versuchen -XX:MaxPermSize=256m und wenn es bestehen bleibt, versuche es -XX:MaxPermSize=512m


35



ich hinzugefügt  -XX: MaxPermSize = 128m (Sie können experimentieren, was am besten funktioniert) VM-Argumente wie ich eclipse ide verwende. In den meisten JVM, Standard PermSize Ist in der Gegend 64 MB Der Speicher ist erschöpft, wenn zu viele Klassen oder eine große Anzahl von Strings im Projekt vorhanden sind.

Für die Sonnenfinsternis ist es auch bei beschrieben Antworten.

SCHRITT 1 : Doppelklicken Sie auf den Tomcat-Server unter Server Tab

enter image description here

SCHRITT 2 : Öffnen Sie den Start Conf und hinzufügen -XX: MaxPermSize = 128m bis zum Ende der bestehenden VM-Argumente.

enter image description here


24



Ich habe mich mit meinem Kopf gegen dieses Problem gestellt, während ich auch eine komplexe Webanwendung bereitgestellt und deimplementiert habe, und dachte, ich würde eine Erklärung und meine Lösung hinzufügen.

Wenn ich eine Anwendung auf Apache Tomcat ausstelle, wird ein neuer ClassLoader für diese App erstellt. Der ClassLoader wird dann verwendet, um alle Klassen der Anwendung zu laden, und bei der Deimplementierung sollte alles gut weggehen. In Wirklichkeit ist es jedoch nicht ganz so einfach.

Eine oder mehrere der Klassen, die während der Lebensdauer der Webanwendung erstellt wurden, enthalten eine statische Referenz, die irgendwo auf der Linie auf den ClassLoader verweist. Da die Referenz ursprünglich statisch ist, wird diese Referenz nicht durch eine Menge von Garbage Collection aufgeräumt - der ClassLoader und alle Klassen, die er geladen hat, sind hier, um zu bleiben.

Und nach ein paar Umsetzungen stoßen wir auf den OutOfMemoryError.

Jetzt ist das ein ziemlich ernstes Problem geworden. Ich könnte sicherstellen, dass Tomcat nach jeder erneuten Bereitstellung neu gestartet wird, aber das führt dazu, dass der gesamte Server heruntergefahren wird und nicht nur die Anwendung neu verteilt wird, was oft nicht möglich ist.

Also habe ich stattdessen eine Lösung in Code zusammengestellt, die auf Apache Tomcat 6.0 funktioniert. Ich habe auf keinem anderen Anwendungsserver getestet und muss dies betonen Dies funktioniert höchstwahrscheinlich nicht ohne Änderung auf einem anderen Anwendungsserver.

Ich würde auch gerne sagen, dass ich persönlich diesen Code hasse Niemand sollte dies als "schnelle Lösung" verwenden, wenn der vorhandene Code geändert werden kann, um ordnungsgemäße Methoden zum Herunterfahren und Bereinigen zu verwenden. Die einzige Zeit, die verwendet werden sollte, ist, wenn es eine externe Bibliothek gibt, von der Ihr Code abhängig ist (in meinem Fall war es ein RADIUS-Client), die keine Möglichkeit bietet, eigene statische Referenzen zu bereinigen.

Wie auch immer, weiter mit dem Code. Dies sollte an dem Punkt aufgerufen werden, an dem die Anwendung das Deployment durchführt - z. B. die Destroy-Methode eines Servlets oder (die bessere Methode) die ContextDestroyed-Methode eines ServletContextListener.

//Get a list of all classes loaded by the current webapp classloader
WebappClassLoader classLoader = (WebappClassLoader) getClass().getClassLoader();
Field classLoaderClassesField = null;
Class clazz = WebappClassLoader.class;
while (classLoaderClassesField == null && clazz != null) {
    try {
        classLoaderClassesField = clazz.getDeclaredField("classes");
    } catch (Exception exception) {
        //do nothing
    }
    clazz = clazz.getSuperclass();
}
classLoaderClassesField.setAccessible(true);

List classes = new ArrayList((Vector)classLoaderClassesField.get(classLoader));

for (Object o : classes) {
    Class c = (Class)o;
    //Make sure you identify only the packages that are holding references to the classloader.
    //Allowing this code to clear all static references will result in all sorts
    //of horrible things (like java segfaulting).
    if (c.getName().startsWith("com.whatever")) {
        //Kill any static references within all these classes.
        for (Field f : c.getDeclaredFields()) {
            if (Modifier.isStatic(f.getModifiers())
                    && !Modifier.isFinal(f.getModifiers())
                    && !f.getType().isPrimitive()) {
                try {
                    f.setAccessible(true);
                    f.set(null, null);
                } catch (Exception exception) {
                    //Log the exception
                }
            }
        }
    }
}

classes.clear();

22



Alternativ können Sie zu JRockit wechseln, welches permgen anders behandelt als sun's jvm. Es hat im Allgemeinen auch eine bessere Leistung.

http://www.oracle.com/technetwork/middleware/jrockit/overview/index.html


15



1) Erhöhen der Speichergröße von PermGen

Das erste, was man tun kann, ist, die Größe des Heap-Space der permanenten Generation größer zu machen. Dies kann nicht mit den üblichen JVM-Argumenten -Xms (Festlegen der anfänglichen Heapgröße) und -Xmx (Festlegen der maximalen Heapgröße) durchgeführt werden, da der Heapspeicherbereich der permanenten Generierung wie bereits erwähnt vollständig vom regulären Java-Heapspeicherbereich getrennt ist. und diese Argumente setzen den Platz für diesen regulären Java-Heap-Space. Es gibt jedoch ähnliche Argumente, die (zumindest mit dem Sun / OpenJDK jvms) verwendet werden können, um die Größe des Halters der permanenten Generation zu vergrößern:

 -XX:MaxPermSize=128m

Standard ist 64 m.

2) Aktivieren Sie Sweeping

Eine andere Möglichkeit, dies zu tun, ist das Entladen von Klassen, damit Ihr PermGen nie leer wird:

-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled

So etwas hat in der Vergangenheit für mich funktioniert. Eine Sache jedoch, gibt es eine erhebliche Leistungseinbußen bei der Verwendung dieser, da Permgen Sweeps machen wie eine extra 2 Anfragen für jede Anfrage, die Sie machen oder etwas in diese Richtung. Sie müssen Ihre Verwendung mit den Kompromissen ausgleichen.

Sie können die Details dieses Fehlers finden.

http://faisalbhagat.blogspot.com/2014/09/java-outofmemoryerror-permgen.html


14