Umgang mit dem Fehler "java.lang.OutOfMemoryError: PermGen space"

1222

Vor kurzem bin ich in meiner Webanwendung auf diesen Fehler gestoßen:

java.lang.OutOfMemoryError: PermGen-Speicherplatz

Es ist eine typische Hibernate / JPA + IceFaces / JSF-Anwendung, die unter Tomcat 6 und JDK 1.6 ausgeführt wird. Anscheinend kann dies nach mehrmaliger erneuter Bereitstellung einer Anwendung auftreten.

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

Chris
quelle
Ich habe stundenlang dagegen gekämpft, aber ich habe keine guten Nachrichten. Siehe meine verwandte Frage: stackoverflow.com/questions/1996088/… Möglicherweise liegt immer noch ein Speicherverlust vor, z. B. werden Klassen nicht mit Müll gesammelt, da Ihr WebAppClassLoader nicht mit Müll gesammelt wird (es gibt einen externen Verweis, der nicht gelöscht wird). Durch Erhöhen von PermGen wird der OutOfMemoryError nur verzögert, und das Zulassen der Speicherbereinigung für Klassen ist eine Voraussetzung, jedoch keine Speicherbereinigung für Klassen, wenn der Klassenlader noch Verweise darauf enthält.
Eran Medan
Ich habe diesen Fehler beim Hinzufügen der Display-Taglib erhalten . Durch das Entfernen wurde auch der Fehler behoben. Warum so?
MasT
Und wie bist du darauf gestoßen?
Thorbjørn Ravn Andersen
13
benutze JDK 1.8: þ Willkommen im MetaSpace
Rytek
Befolgen Sie bei Verwendung von Windows diese Anweisungen, anstatt zu versuchen, die Flags manuell in den Konfigurationsdateien zu setzen. Dadurch werden die Werte in der Registrierung ordnungsgemäß festgelegt, die von Tomcat zur Laufzeit aufgerufen werden sollen. stackoverflow.com/questions/21104340/…
MacGyver

Antworten:

563

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

-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled

Sie können dies tun, indem Sie den Tomcat-Dienst herunterfahren, dann in das Tomcat / bin-Verzeichnis wechseln 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 eine Fehlermeldung erhalten, existiert der angegebene Dienst nicht als installierter Dienst, den Sie ausführen sollten:

tomcat6w //ES//servicename

Dabei ist Servicename der Name des Servers in der Datei services.msc

Quelle: Orxs Kommentar zu Eric's Agile Answers .

Chris
quelle
39
Der folgende Artikel schlägt ebenfalls -XX: + UseConcMarkSweepGC und -XX: MaxPermSize = 128m vor. my.opera.com/karmazilla/blog/2007/03/13/…
Taylor Leese
30
-XX: + CMSPermGenSweepingEnabled Diese Option verringert die Leistung. Dadurch dauert jede Anfrage auf unseren Systemen dreimal länger als gewöhnlich. Vorsichtig verwenden.
Eldelshell
10
hat für mich gearbeitet - danke - ich mache das unter Ubuntu 10.10 mit Tomcat6 - Ich habe eine neue Datei erstellt: /usr/share/tomcat6/bin/setenv.sh und die folgende Zeile hinzugefügt: JAVA_OPTS = "- Xms256m -Xmx512m - XX: + CMSClassUnloadingEnabled -XX: + CMSPermGenSweepingEnabled "- Tomcat neu gestartet mit: sudo /etc/init.d/tomcat6 start
sami
24
Beim Start von Tomcat 6.0.29 aus meiner Catalina.out-Protokolldatei: "Bitte verwenden Sie CMSClassUnloadingEnabled in Zukunft anstelle von CMSPermGenSweepingEnabled in der Zukunft"
knb
222
Zunächst wäre es toll zu erklären, was diese Flags wirklich tun. Nur zu sagen: "Mach das und genieße" ist meiner Meinung nach nicht genug.
Nikem
251

Versuchen Sie es -XX:MaxPermSize=128Mlieber als -XX:MaxPermGen=128M.

Ich kann die genaue Verwendung dieses Speicherpools nicht sagen, aber es hängt mit der Anzahl der in die JVM geladenen Klassen zusammen. (Durch Aktivieren des Entladens von Klassen für Tomcat kann das Problem behoben werden.) Wenn Ihre Anwendungen beim Ausführen Klassen generieren und kompilieren, ist es wahrscheinlicher, dass ein Speicherpool benötigt wird, der größer als der Standard ist.

toesterdahl
quelle
9
Tatsächlich wird dies nur OOMError verschieben. Siehe die Antwort unten, die von anon mit zwei Links zum frankkieviet-Blog gestartet wurde.
Rade_303
Diese Optionen werden hier erklärt: oracle.com/technetwork/java/javase/tech/…
amos
153

PermGen-Fehler des App-Servers, die nach mehreren Bereitstellungen auftreten, werden höchstwahrscheinlich durch Verweise verursacht, die der Container in den Klassenladeprogrammen Ihrer alten Apps enthält. Wenn Sie beispielsweise eine benutzerdefinierte Klasse auf Protokollebene verwenden, werden Referenzen vom Klassenladeprogramm des App-Servers gespeichert. Sie können diese Lecks zwischen Klassenladern erkennen, indem Sie moderne (JDK6 +) JVM-Analysetools wie jmap und jhat verwenden, um zu überprüfen, welche Klassen weiterhin in Ihrer App enthalten sind, und ihre Verwendung neu zu gestalten oder zu beseitigen. Übliche Verdächtige sind Datenbanken, Logger und andere Bibliotheken auf Basis-Framework-Ebene.

Siehe Classloader-Lecks: Die gefürchtete Ausnahme "java.lang.OutOfMemoryError: PermGen space" und insbesondere der nachfolgende Beitrag .

anon
quelle
3
Dies ist nur eine echte Lösung des Problems, die in einigen Fällen zu schwer zu implementieren ist.
Rade_303
Eine weitere sehr gute Quelle ist people.apache.org/~markt/presentations/… (vom Tomcat Release Manager !!).
Gavenkoa
22
Während dies theoretisch die Frage beantworten kann, wäre es vorzuziehen , die wesentlichen Teile der Antwort hier aufzunehmen und den Link als Referenz bereitzustellen.
Joachim Sauer
68

Häufige Fehler, die Menschen machen, sind der Gedanke, dass Heap Space und Permgen Space gleich sind, was überhaupt nicht stimmt. Sie könnten noch viel Speicherplatz auf dem Heap haben, aber in permgen kann immer noch nicht genügend Speicherplatz vorhanden sein.

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 gespeichert und sie werden mit Müll gesammelt, wenn der Classloader, der sie geladen hat, für die Garbage Collection bereit ist. In dem Fall, dass Classloader einen Speicherverlust aufweist, bleiben alle von ihm geladenen Klassen im Speicher und verursachen PermGen outofmemory, sobald Sie ihn einige Male wiederholen. Das klassische Beispiel ist Java.lang.OutOfMemoryError: PermGen Space in Tomcat .

Nun gibt es zwei Möglichkeiten, dies zu lösen:
1. Finden Sie die Ursache für einen Speicherverlust oder wenn ein Speicherverlust vorliegt.
2. Erhöhen Sie die Größe von PermGen Space mithilfe von JVM param -XX:MaxPermSizeund -XX:PermSize.

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

Peter
quelle
3
Wie übergeben Sie param -XX:MaxPermSize and -XX:PermSize? Ich kann nicht finden catalina.bat. Meine Tomcat-Version ist 5.5.26.
Deckard
Wie finde ich die Speicherlecks des Klassenladeprogramms? Empfehlen Sie ein Werkzeug?
Amit
@amit für Tool-Empfehlungen finden Sie in der Antwort des Community-Wikis zu dieser Frage.
Barett
@Deckard geht in das Tomcat / bin-Verzeichnis und führt tomcat6w.exe aus. Fügen Sie auf der Registerkarte "Java" die Argumente zum Feld "Java-Optionen" hinzu. Klicken Sie auf "OK"
Zeb
43

Verwenden Sie den Befehlszeilenparameter -XX:MaxPermSize=128mfür eine Sun-JVM (ersetzen Sie offensichtlich 128 durch die gewünschte Größe).

user17163
quelle
9
Das einzige Problem ist, dass Sie nur das Unvermeidliche verzögern - irgendwann wird Ihnen dort auch die Kopffreiheit ausgehen. Es ist eine großartige pragmatische Lösung, aber sie löst sie nicht dauerhaft.
Tim Howland
Das Gleiche passiert in Eclipse und jedes Mal, wenn Sie viele dynamische Klassen laden. Die Klassenlader sind nicht entsorgt und leben für alle Ewigkeit in der permanenten Generation
Matt
1
Mir ging PermGen aus, als ich einen besonders großen Hudson-Job ausführte ... das hat es für mich behoben.
HDave
10
@TimHowland, es kann eine dauerhafte Lösung sein, wenn die Hauptursache nicht ein Classloader-Leck ist, sondern einfach zu viele Klassen / statische Daten in Ihrer Web-App.
Péter Török
habe das gleiche Problem wie HDave beim Erstellen von Jenkins / Hudson aus dem Quellcode.
Louisgab
38

Versuchen Sie -XX:MaxPermSize=256mes und versuchen Sie es, wenn es weiterhin besteht-XX:MaxPermSize=512m

Bassist
quelle
56
und wenn es immer noch versucht, versuchen Sie es XX:MaxPermSize=1024m:)
igo
38
und wenn es immer noch besteht, versuchen Sie es mit XX: MaxPermSize = 2048m :)
Thomas
16
Und wenn es immer noch so bleibt, überdenken Sie Ihre Bewerbung! Oder versuchen Sie XX: MaxPermSize = 4096m :)
Jonathan Airey
17
Sie können auch 8192m versuchen, aber das ist ein bisschen übertrieben
Jacek Pietal
12
Overkill in der Tat - 640 KB sollten für jeden ausreichen!
Joel Purra
28

Ich fügte hinzu -XX: MaxPermSize = 128m VM-Argumente (Sie können experimentieren, was am besten funktioniert), da ich eclipse ide verwende. In den meisten JVM- Versionen beträgt die Standard-PermSize etwa 64 MB , wenn nicht genügend Speicher vorhanden ist, wenn das Projekt zu viele Klassen oder eine große Anzahl von Zeichenfolgen enthält.

Für Eclipse wird es auch bei der Antwort beschrieben .

SCHRITT 1 : Doppelklicken Sie auf den Tomcatbediener auf Servern Tab

Geben Sie hier die Bildbeschreibung ein

SCHRITT 2 : Öffnen Sie Launch Conf und fügen Sie -XX: MaxPermSize = 128mam Ende vorhandene VM-Argumente hinzu .

Geben Sie hier die Bildbeschreibung ein

betend
quelle
1
Vielen Dank für Ihre ausführlichste Antwort (Hinweis: Klicken Sie auf "Startkonfiguration öffnen", um das Fenster "Konfiguration bearbeiten" zu öffnen ... aber ich habe die folgenden Parameter verwendet: "-XX: + CMSClassUnloadingEnabled -XX: + CMSPermGenSweepingEnabled"
Chris Sim
23

Ich habe mich beim Bereitstellen und Aufheben der Bereitstellung einer komplexen Webanwendung mit diesem Problem auseinandergesetzt und dachte, ich würde eine Erklärung und meine Lösung hinzufügen.

Wenn ich eine Anwendung auf Apache Tomcat bereitstelle, wird ein neuer ClassLoader für diese App erstellt. Der ClassLoader wird dann verwendet, um alle Klassen der Anwendung zu laden, und beim Aufheben der Bereitstellung sollte alles gut gehen. 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 entlang der Linie auf den ClassLoader verweist. Da die Referenz ursprünglich statisch ist, wird diese Referenz durch keine Speicherbereinigung bereinigt. Der ClassLoader und alle geladenen Klassen bleiben erhalten.

Und nach ein paar Neueinsätzen stoßen wir auf den OutOfMemoryError.

Nun ist dies ein ziemlich ernstes Problem geworden. Ich könnte sicherstellen, dass Tomcat nach jeder erneuten Bereitstellung neu gestartet wird, aber dadurch wird der gesamte Server heruntergefahren und nicht nur die neu bereitgestellte Anwendung, was häufig nicht möglich ist.

Also habe ich stattdessen eine Lösung in Code zusammengestellt, die unter Apache Tomcat 6.0 funktioniert. Ich habe auf keinem anderen Anwendungsserver getestet und muss betonen, dass dies ohne Änderungen auf einem anderen Anwendungsserver sehr wahrscheinlich nicht funktioniert .

Ich möchte auch sagen, dass ich diesen Code persönlich hasse und dass niemand dies als "schnelle Lösung" verwenden sollte, wenn der vorhandene Code geändert werden kann, um die richtigen Methoden zum Herunterfahren und Bereinigen zu verwenden . Dies sollte nur verwendet werden, 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, seine eigenen statischen Referenzen zu bereinigen.

Wie auch immer, weiter mit dem Code. Dies sollte an dem Punkt aufgerufen werden, an dem die Anwendung nicht bereitgestellt wird, z. B. die Zerstörungsmethode eines Servlets oder (der bessere Ansatz) 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();
Edward Torbett
quelle
Ich weiß, dass es über 8 Jahre her ist, seit ich das geschrieben habe, aber irgendwo zwischen damals und heute habe ich eine tiefere Ursache und Lösung gefunden. Während alle Klassen in der Webanwendung dem Kontextklassenlader gehören, gehört der Thread, der den Rückruf zum Starten und Herunterfahren aufruft, dem übergeordneten Klassenlader. Dies bedeutet, dass, wenn der Shutdown-Code eine threadlokale Variable initialisiert, der übergeordnete Klassenladeprogramm einen Verweis auf den Kontextklassenladeprogramm enthält, wodurch eine gute Bereinigung verhindert wird.
Edward Torbett
Glücklicherweise gibt es eine sehr einfache Lösung: Verschieben Sie den gesamten Code, der sich derzeit in der Shutdown-Methode befindet, in die Ausführungsmethode eines neuen Thread-Objekts. Starten Sie dann in der Shutdown-Methode diesen Thread und warten Sie, bis er abgeschlossen ist. Der Bereinigungscode wird identisch ausgeführt, aber alle threadlokalen Variablen bleiben an den Kontextklassenlader gebunden, anstatt zu lecken.
Edward Torbett
20

Die java.lang.OutOfMemoryError: PermGenLeertaste zeigt an, dass der Speicherbereich der permanenten Generation erschöpft ist.

Alle Java-Anwendungen dürfen nur eine begrenzte Menge an Speicher verwenden. Die genaue Speichermenge, die Ihre bestimmte Anwendung verwenden kann, wird beim Start der Anwendung angegeben.

Der Java-Speicher ist in verschiedene Regionen unterteilt, die im folgenden Bild zu sehen sind:

Geben Sie hier die Bildbeschreibung ein

Metaspace: Ein neuer Erinnerungsraum wird geboren

Die JDK 8 HotSpot-JVM verwendet jetzt nativen Speicher für die Darstellung von Klassenmetadaten und heißt Metaspace. ähnlich den Oracle JRockit- und IBM JVMs.

Die gute Nachricht ist, dass Sie keine java.lang.OutOfMemoryError: PermGenSpeicherplatzprobleme mehr haben und diesen Speicherplatz nicht mehr mit Java_8_Download oder höher optimieren und überwachen müssen .

Santosh Jadi
quelle
17

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

Das erste, was man tun kann, ist, den Heap-Speicher der permanenten Generation zu vergrößern. Dies ist mit den üblichen JVM-Argumenten –Xms (anfängliche Heap-Größe festlegen) und –Xmx (maximale Heap-Größe festlegen) nicht möglich, da der Heap-Speicher der permanenten Generierung wie erwähnt vollständig vom regulären Java-Heap-Speicher getrennt ist und diese Argumente festgelegt sind Der Speicherplatz für diesen regulären Java-Heapspeicher. Es gibt jedoch ähnliche Argumente, die verwendet werden können (zumindest mit den Sun / OpenJDK-JVMS), um die Größe des permanenten Generierungshaufens zu vergrößern:

 -XX:MaxPermSize=128m

Standard ist 64m.

2) Aktivieren Sie das Kehren

Eine andere Möglichkeit, dies endgültig zu erledigen, besteht darin, das Entladen von Klassen zuzulassen, damit Ihr PermGen nie ausgeht:

-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled

Solche Sachen haben in der Vergangenheit für mich Magie gewirkt. Eine Sache ist jedoch, dass die Verwendung dieser Produkte einen erheblichen Leistungskompromiss darstellt, da Permgen-Sweeps für jede von Ihnen gestellte Anfrage oder etwas in dieser Richtung zusätzliche 2 Anfragen stellen. Sie müssen Ihre Verwendung mit den Kompromissen in Einklang bringen.

Sie können die Details dieses Fehlers finden.

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

faisalbhagat
quelle
Großartiger Beitrag von @faisalbhagat faisalbhagat.blogspot.com/2014/09/…
leomeurer
Option 2 ist großartig, aber seien Sie gewarnt, dass es nicht in Produktionsumgebungen verwendet werden sollte. In der Regel am besten nur für Entwicklungsumgebungen. Allerdings wird PermGen ab Java 8 entfernt openjdk.java.net/jeps/122
hdost
16

Alternativ können Sie zu JRockit wechseln, das das Permgen anders handhabt als das JVM von Sun. Es hat im Allgemeinen auch eine bessere Leistung.

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

Jeremy
quelle
4
JRockit hat zwar kein PermGen, aber das wird auf lange Sicht nicht helfen. Du wirst java.lang.OutOfMemoryError: There is insufficient native memorystattdessen bekommen .
Stracktracer
14

Ich hatte das Problem, über das wir hier sprechen. Mein Szenario ist Eclipse-Helios + Tomcat + JSF. Sie haben eine einfache Anwendung für Tomcat bereitgestellt. Ich habe hier das gleiche Problem gezeigt und es wie folgt gelöst.

In Eclipse gehen Sie zur Registerkarte Server. Doppelklicken Sie in meinem Fall auf den registrierten Server. Tomcat 7.0 öffnet meinen Dateiserver. Allgemeine Registrierungsinformationen. Klicken Sie im Abschnitt "Allgemeine Informationen" auf den Link "Startkonfiguration öffnen" . Dadurch wird die Ausführung von Serveroptionen auf der Registerkarte "Argumente" in VM-Argumenten geöffnet, die am Ende dieser beiden Einträge hinzugefügt wurden

-XX: MaxPermSize = 512m
-XX: PermSize = 512m

und fertig.

Hugo Mendoza
quelle
14

Die einfachste Antwort ist heutzutage die Verwendung von Java 8.

Es reserviert nicht mehr ausschließlich Speicher für PermGen-Speicherplatz, sodass sich der PermGen-Speicher mit dem regulären Speicherpool vermischen kann.

Beachten Sie, dass Sie alle nicht standardmäßigen -XXPermGen...=...JVM-Startparameter entfernen müssen, wenn Sie nicht möchten, dass Java 8 sich darüber beschwert, dass sie nichts tun.

Edwin Buck
quelle
Hallo, diese Antwort wurde bereits gegeben: stackoverflow.com/a/22897121/505893 . Bitte löschen Sie Ihre Antwort aus Gründen der Übersichtlichkeit. Bei Bedarf können Sie die von mir erwähnte Antwort verbessern. Danke;)
bläulich
6
@bluish Danke, dass du darauf hingewiesen hast; Diese Antwort geht jedoch seitwärts, wenn es um OutOfMemoryExceptions und undichte Metadaten geht. Außerdem werden die sehr wichtigen Punkte beim Entfernen der PermGen-Option nicht erwähnt. Kurz gesagt, ich bin mir nicht sicher, ob ich die Antwort verbessern, sondern umschreiben würde. Wenn es nur eine schnelle Nachbesserung wäre, würde ich mich weniger zögern, aber es sieht so aus, als wäre es viel mehr als eine schnelle Nachbesserung, und ich würde es hassen, den ursprünglichen Autor zu beleidigen. Trotzdem ist diese Antwortliste das Abendessen eines Hundes, und vielleicht wäre es sowieso am besten, meinen Beitrag zu töten.
Edwin Buck
8
  1. Öffnen Sie tomcat7w aus dem bin-Verzeichnis von Tomcat oder geben Sie im Startmenü Monitor Tomcat ein (ein Fenster mit Registerkarten wird mit verschiedenen Dienstinformationen geöffnet).
  2. Fügen Sie im Textbereich Java-Optionen diese Zeile hinzu:

    -XX:MaxPermSize=128m
  3. Setzen Sie den anfänglichen Speicherpool auf 1024 (optional).
  4. Stellen Sie den maximalen Speicherpool auf 1024 ein (optional).
  5. OK klicken.
  6. Starten Sie den Tomcat-Dienst neu.
Glück
quelle
6

Ein Fehler im Perm-Speicherplatz tritt aufgrund der Verwendung eines großen Speicherplatzes auf, anstatt dass jvm Speicherplatz für die Ausführung des Codes bereitstellt.

Die beste Lösung für dieses Problem unter UNIX-Betriebssystemen besteht darin, einige Konfigurationen in der Bash-Datei zu ändern. Die folgenden Schritte lösen das Problem.

Führen Sie den Befehl gedit .bashrcauf dem Terminal aus.

Erstellen Sie eine JAVA_OTPSVariable mit folgendem Wert:

export JAVA_OPTS="-XX:PermSize=256m -XX:MaxPermSize=512m"

Speichern Sie die Bash-Datei. Führen Sie den Befehl exec bash auf dem Terminal aus. Starten Sie den Server neu.

Ich hoffe, dieser Ansatz wird bei Ihrem Problem funktionieren. Wenn Sie eine Java-Version unter 8 verwenden, tritt dieses Problem manchmal auf. Wenn Sie jedoch Java 8 verwenden, tritt das Problem nie auf.

Darshan
quelle
5

Das Erhöhen der Größe der permanenten Generierung oder das Ändern der GC-Parameter hilft NICHT, wenn Sie einen echten Speicherverlust haben. Wenn Ihre Anwendung oder eine Bibliothek eines Drittanbieters verwendet wird, besteht die einzige echte und dauerhafte Lösung darin, dieses Leck zu finden und zu beheben. Es gibt eine Reihe von Tools, die Ihnen helfen können. Eines der jüngsten ist Plumbr , das gerade eine neue Version mit den erforderlichen Funktionen veröffentlicht hat.

Nikem
quelle
5

Wenn Sie log4j in Ihrer Webanwendung verwenden, überprüfen Sie diesen Absatz in der log4j- Dokumentation .

Es scheint, dass Sie bei der Verwendung PropertyConfigurator.configureAndWatch("log4j.properties")Speicherverluste verursachen, wenn Sie die Bereitstellung Ihrer Webanwendung aufheben.

Sermojohn
quelle
4

Ich habe eine Kombination von Hibernate + Eclipse RCP, versucht , mit -XX:MaxPermSize=512mund -XX:PermSize=512mund es scheint , für mich zu arbeiten.

Pankaj Shinde
quelle
4

Stellen Sie ein -XX:PermSize=64m -XX:MaxPermSize=128m. Später können Sie auch versuchen, zu erhöhen MaxPermSize. Hoffe es wird funktionieren. Das gleiche funktioniert bei mir. Das Einstellen MaxPermSizehat bei mir nicht funktioniert.

Sandeep
quelle
4

Ich habe mehrere Antworten ausprobiert und das einzige, was den Job letztendlich erledigt hat, war diese Konfiguration für das Compiler-Plugin im POM:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.3.2</version>
    <configuration>
        <fork>true</fork>
        <meminitial>128m</meminitial>
        <maxmem>512m</maxmem>
        <source>1.6</source>
        <target>1.6</target>
        <!-- prevent PermGen space out of memory exception -->
        <!-- <argLine>-Xmx512m -XX:MaxPermSize=512m</argLine> -->
    </configuration>
</plugin>

hoffe dieser hilft.

Kiwilisk
quelle
"argLine" wird vom maven-compiler-plugin 2.4 nicht erkannt. Es wird nur "compilerArgument" unterstützt, was zu einem Fehler führt: <compilerArgument> -XX: MaxPermSize = 256m </ compilerArgument> [ERROR] Fehler beim Ausführen von javac, konnte jedoch den Fehler nicht analysieren: javac: ungültiges Flag: -XX: MaxPermSize = 256m Verwendung : javac <Optionen> <Quelldateien>
Alex
Wenn Ihre Kompilierungsphase kein Permgen mehr hat, setzen Sie das <compilerArgument> auf das Maven-Compiler-Plugin. Wenn den Unit-Tests das Permgen ausgeht, setzen Sie <argLine> im Maven-Surefire-Plugin
QWERTY
4

löste dies auch für mich auf; Ich bemerkte jedoch, dass die Servlet-Neustartzeiten viel schlechter waren. Während es in der Produktion besser war, war es eine Art Entwicklungsproblem.

Tim Howland
quelle
3

Die Konfiguration des Speichers hängt von der Art Ihrer App ab.

Was machst du gerade?

Wie viele Transaktionen wurden ausgeführt?

Wie viele Daten laden Sie?

usw.

usw.

usw

Wahrscheinlich könnten Sie Ihre App profilieren und einige Module aus Ihrer App bereinigen.

Anscheinend kann dies nach mehrmaliger erneuter Bereitstellung einer Anwendung auftreten

Tomcat verfügt über eine Hot-Bereitstellung, verbraucht jedoch Speicher. Starten Sie Ihren Container von Zeit zu Zeit neu. Außerdem müssen Sie wissen, wie viel Speicher für die Ausführung im Produktionsmodus erforderlich ist. Dies scheint ein guter Zeitpunkt für diese Recherche zu sein.

OscarRyz
quelle
3

Sie sagen, dass die neueste Version von Tomcat (6.0.28 oder 6.0.29) die Aufgabe der erneuten Bereitstellung von Servlets viel besser erledigt .

Tony Ennis
quelle
3

Ich habe genau das gleiche Problem, aber leider hat keine der vorgeschlagenen Lösungen wirklich für mich funktioniert. Das Problem trat während der Bereitstellung nicht auf, und ich führte auch keine Hot-Bereitstellungen durch.

In meinem Fall trat das Problem jedes Mal zum gleichen Zeitpunkt während der Ausführung meiner Webanwendung auf, während eine Verbindung (über den Ruhezustand) zur Datenbank hergestellt wurde.

Dieser Link (ebenfalls zuvor erwähnt) lieferte genügend Insider-Informationen, um das Problem zu beheben. Das Verschieben des jdbc- (mysql) -Treibers aus dem WEB-INF in den Ordner jre / lib / ext / scheint das Problem gelöst zu haben. Dies ist nicht die ideale Lösung, da Sie für ein Upgrade auf eine neuere JRE den Treiber neu installieren müssen. Ein weiterer Kandidat, der ähnliche Probleme verursachen könnte, ist log4j. Vielleicht möchten Sie auch diesen verschieben

Labyrinth
quelle
Wenn Sie den Treiber nicht in jre / lib / ext aufnehmen möchten, können Sie wahrscheinlich dieselben Ergebnisse erzielen, indem Sie den Treiber in Ihren Container-Startklassenpfad aufnehmen. java -cp /path/to/jdbc-mysql-driver.jar:/path/to/container/bootstrap.jar container.Start
Scot
3

Der erste Schritt in einem solchen Fall besteht darin, zu überprüfen, ob der GC Klassen aus PermGen entladen darf. Die Standard-JVM ist in dieser Hinsicht eher konservativ - Klassen werden geboren, um für immer zu leben. Nach dem Laden bleiben die Klassen im Speicher, auch wenn sie von keinem Code mehr verwendet werden. Dies kann zu einem Problem werden, wenn die Anwendung viele Klassen dynamisch erstellt und die generierten Klassen für längere Zeiträume nicht benötigt werden. In einem solchen Fall kann es hilfreich sein, der JVM das Entladen von Klassendefinitionen zu ermöglichen. Dies kann erreicht werden, indem Sie Ihren Startskripten nur einen Konfigurationsparameter hinzufügen:

-XX:+CMSClassUnloadingEnabled

Standardmäßig ist dies auf false gesetzt. Um dies zu aktivieren, müssen Sie die folgende Option in Java-Optionen explizit festlegen. Wenn Sie CMSClassUnloadingEnabled aktivieren, durchsucht GC auch PermGen und entfernt Klassen, die nicht mehr verwendet werden. Beachten Sie, dass diese Option nur funktioniert, wenn UseConcMarkSweepGC auch mit der folgenden Option aktiviert ist. Wenn Sie also ParallelGC oder, Gott bewahre, Serial GC ausführen, stellen Sie sicher, dass Sie Ihren GC auf CMS eingestellt haben, indem Sie Folgendes angeben:

-XX:+UseConcMarkSweepGC
sendon1982
quelle
3

Das Zuweisen von mehr Speicher für Tomcat ist NICHT die richtige Lösung.

Die richtige Lösung besteht darin, eine Bereinigung durchzuführen, nachdem der Kontext zerstört und neu erstellt wurde (die Hot-Bereitstellung). Die Lösung besteht darin, die Speicherlecks zu stoppen.

Wenn Ihr Tomcat / Webapp-Server Ihnen mitteilt, dass die Registrierung von Treibern (JDBC) fehlgeschlagen ist, heben Sie die Registrierung auf. Dadurch werden Speicherlecks gestoppt.

Sie können einen ServletContextListener erstellen und in Ihrer web.xml konfigurieren. Hier ist ein Beispiel für einen ServletContextListener:

import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.apache.log4j.Logger;

import com.mysql.jdbc.AbandonedConnectionCleanupThread;

/**
 * 
 * @author alejandro.tkachuk / calculistik.com
 *
 */
public class AppContextListener implements ServletContextListener {

    private static final Logger logger = Logger.getLogger(AppContextListener.class);

    @Override
    public void contextInitialized(ServletContextEvent arg0) {
        logger.info("AppContextListener started");
    }

    @Override
    public void contextDestroyed(ServletContextEvent arg0) {
        logger.info("AppContextListener destroyed");

        // manually unregister the JDBC drivers
        Enumeration<Driver> drivers = DriverManager.getDrivers();
        while (drivers.hasMoreElements()) {
            Driver driver = drivers.nextElement();
            try {
                DriverManager.deregisterDriver(driver);
                logger.info(String.format("Unregistering jdbc driver: %s", driver));
            } catch (SQLException e) {
                logger.info(String.format("Error unregistering driver %s", driver), e);
            }

        }

        // manually shutdown clean up threads
        try {
            AbandonedConnectionCleanupThread.shutdown();
            logger.info("Shutting down AbandonedConnectionCleanupThread");
        } catch (InterruptedException e) {
            logger.warn("SEVERE problem shutting down AbandonedConnectionCleanupThread: ", e);
            e.printStackTrace();
        }        
    }
}

Und hier konfigurieren Sie es in Ihrer web.xml:

<listener>
    <listener-class>
        com.calculistik.mediweb.context.AppContextListener 
    </listener-class>
</listener>  
Alejandro Pablo Tkachuk
quelle
2

"Sie" sind falsch, weil ich 6.0.29 verwende und das gleiche Problem habe, auch nachdem ich alle Optionen festgelegt habe. Wie Tim Howland oben sagte, haben diese Optionen nur das Unvermeidliche aufgeschoben. Sie ermöglichen es mir, dreimal neu bereitzustellen, bevor der Fehler auftritt, anstatt jedes Mal, wenn ich erneut bereitstelle.

Ross Völker
quelle
2

Falls Sie dies in der Eclipse IDE bekommen, auch nach der Einstellung der Parameter --launcher.XXMaxPermSize, -XX:MaxPermSizeetc., immer noch , wenn Sie die gleiche Fehlermeldung erhalten, ist es sehr wahrscheinlich ist , dass die Eklipse eine fehlerhafte Version von JRE verwendet , die von einigen installiert worden wäre , Anwendungen von Drittanbietern und auf Standard gesetzt. Diese fehlerhaften Versionen übernehmen die PermSize-Parameter nicht und unabhängig von Ihrer Einstellung werden diese Speicherfehler immer wieder angezeigt. Fügen Sie also in Ihrer eclipse.ini die folgenden Parameter hinzu:

-vm <path to the right JRE directory>/<name of javaw executable>

Stellen Sie außerdem sicher, dass Sie die Standard-JRE in den Einstellungen in der Eclipse auf die richtige Version von Java einstellen.

Hrishikesh Kumar
quelle
2

Der einzige Weg, der für mich funktionierte, war mit der JRockit JVM. Ich habe MyEclipse 8.6.

Der Heap der JVM speichert alle Objekte, die von einem laufenden Java-Programm generiert wurden. Java verwendet den newOperator zum Erstellen von Objekten, und der Speicher für neue Objekte wird zur Laufzeit auf dem Heap zugewiesen. Die Speicherbereinigung ist der Mechanismus zum automatischen Freigeben des Speichers der Objekte, auf die das Programm nicht mehr verweist.

Benutzer1985910
quelle
2

Ich hatte ein ähnliches Problem. Meins ist JDK 7 + Maven 3.0.2 + Struts 2.0 + Google GUICE-Projekt auf Basis der Abhängigkeitsinjektion.

Wann immer ich versuchte, den mvn clean packageBefehl auszuführen , wurde der folgende Fehler angezeigt und "BUILD FAILURE" trat auf

org.apache.maven.surefire.util.SurefireReflectionException: java.lang.reflect.InvocationTargetException; verschachtelte Ausnahme ist java.lang.reflect.InvocationTargetException: null java.lang.reflect.InvocationTargetException Auslöser: java.lang.OutOfMemoryError: PermGen space

Ich habe alle oben genannten nützlichen Tipps und Tricks ausprobiert, aber leider hat keiner für mich funktioniert. Was für mich funktioniert hat, wird Schritt für Schritt beschrieben: =>

  1. Gehen Sie zu Ihrer pom.xml
  2. Suchen nach <artifactId>maven-surefire-plugin</artifactId>
  3. Fügen Sie ein neues <configuration>Element und dann ein <argLine>Unterelement hinzu, in dem -Xmx512m -XX:MaxPermSize=256mwie unten gezeigt übergeben wird =>

<configuration> <argLine>-Xmx512m -XX:MaxPermSize=256m</argLine> </configuration>

Hoffe es hilft, viel Spaß beim Programmieren :)

NIKHIL CHAURASIA
quelle