Recentemente mi sono imbattuto in questo errore nella mia applicazione web:
Java.lang.OutOfMemoryError: PermGen space
È una tipica applicazione Hibernate/JPA + IceFaces/JSF in esecuzione su Tomcat 6 e JDK 1.6. Apparentemente questo può accadere dopo aver ridistribuito un'applicazione alcune volte.
Cosa lo causa e cosa si può fare per evitarlo? Come posso risolvere il problema?
La soluzione era quella di aggiungere questi flag alla riga di comando JVM all'avvio di Tomcat:
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
È possibile farlo chiudendo il servizio Tomcat, quindi andando nella directory Tomcat/bin ed eseguendo Tomcat6w.exe. Sotto la scheda "Java", aggiungi gli argomenti alla casella "Opzioni Java". Fare clic su "OK" e quindi riavviare il servizio.
Se si verifica un errore il servizio specificato non esiste come servizio installato , è necessario eseguire:
Tomcat6w //ES//servicename
dove servicename è il nome del server visualizzato in services.msc
Fonte: commento di orx su Eric's Agile Answers .
Faresti meglio a provare -XX:MaxPermSize=128M
piuttosto che -XX:MaxPermGen=128M
.
Non posso dire l'uso preciso di questo pool di memoria, ma ha a che fare con il numero di classi caricate nella JVM. (Pertanto, lo scaricamento della classe per Tomcat può risolvere il problema.) Se le applicazioni generano e compila classi in esecuzione è più probabile che sia necessario un lotto di memoria più grande del valore predefinito.
Gli errori comuni che le persone fanno è pensare che lo spazio dell'heap e lo spazio del permgen siano gli stessi, il che non è affatto vero. Si potrebbe avere molto spazio rimanente nell'heap ma è ancora possibile esaurire la memoria in permgen.
Le cause comuni di OutofMemory in PermGen sono ClassLoader. Ogni volta che una classe viene caricata in JVM, tutti i suoi metadati, insieme a Classloader, vengono mantenuti nell'area PermGen e verranno raccolti automaticamente quando il Classloader che li ha caricati è pronto per la garbage collection. In Case Classloader si verifica una perdita di memoria rispetto a tutte le classi caricate da essa e rimane in memoria e causa permGen outofmemory dopo averlo ripetuto un paio di volte. L'esempio classico è Java.lang.OutOfMemoryError: PermGen Space in Tomcat .
Ora ci sono due modi per risolvere questo:
1. Trova la causa di Perdita di memoria o se c'è qualche perdita di memoria.
2. Aumentare le dimensioni di PermGen Space utilizzando JVM param -XX:MaxPermSize
e -XX:PermSize
.
Puoi anche controllare 2 Soluzione di Java.lang.OutOfMemoryError in Java per maggiori dettagli.
Usa il parametro della riga di comando -XX:MaxPermSize=128m
per una JVM Sun (ovviamente sostituendo 128 per qualsiasi dimensione tu abbia bisogno).
Prova -XX:MaxPermSize=256m
e se persiste, prova -XX:MaxPermSize=512m
I added-XX: MaxPermSize = 128m
(puoi sperimentare quale funziona meglio) a VM Arguments dato che sto usando Eclipse ide. Nella maggior parte di JVM, il valore predefinito PermSize è intorno 64MB che esaurisce la memoria se ce ne sono troppi classi o un numero enorme di stringhe nel progetto.
Per Eclipse, è anche descritto a answer .
PASSAGGIO 1: doppio clic sul server Tomcat nella scheda Server
STEP 2: Apri lancio Conf e aggiungi -XX: MaxPermSize = 128m
alla fine di esistente Argomenti VM .
Mi sono scontrato con questo problema mentre distribuivo e disassocando anche una complessa applicazione web, e ho pensato di aggiungere una spiegazione e la mia soluzione.
Quando distribuisco un'applicazione su Apache Tomcat, viene creato un nuovo ClassLoader per quell'app. Il ClassLoader viene quindi utilizzato per caricare tutte le classi dell'applicazione e, in undecloy, tutto dovrebbe andare bene. Tuttavia, in realtà non è così semplice.
Una o più delle classi create durante la vita dell'applicazione Web contiene un riferimento statico che, in qualche punto della linea, fa riferimento a ClassLoader. Poiché il riferimento è in origine statico, nessuna quantità di garbage collecting pulirà questo riferimento - il ClassLoader e tutte le classi caricate sono qui per rimanere.
E dopo un paio di redeploys, incontriamo OutOfMemoryError.
Ora questo è diventato un problema abbastanza serio. Potrei assicurarmi che Tomcat venga riavviato dopo ogni ridistribuzione, ma che rimuova l'intero server, anziché solo l'applicazione che viene ridistribuita, cosa che spesso non è fattibile.
Così, invece, ho messo insieme una soluzione in codice, che funziona su Apache Tomcat 6.0. Non ho provato su altri server di applicazioni e devo sottolineare che è molto probabile che non funzioni senza modifiche su nessun altro server delle applicazioni .
Vorrei anche dire che personalmente odio questo codice, e che nessuno dovrebbe usarlo come una "soluzione rapida" se il codice esistente può essere modificato per usare i metodi appropriati di spegnimento e pulizia . L'unica volta che questo dovrebbe essere usato è se c'è una libreria esterna dalla quale dipende il codice (nel mio caso, era un RADIUS client) che non fornisce un mezzo per ripulire i propri riferimenti statici.
Ad ogni modo, con il codice. Questo dovrebbe essere chiamato nel punto in cui l'applicazione sta decomponendo, come il metodo destroy di un servlet o (l'approccio migliore) il metodo contextDestroyed di 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();
La prima cosa che si può fare è rendere più grande la dimensione dello spazio heap della generazione permanente. Questo non può essere fatto con le solite argomentazioni di XM (imposta la dimensione iniziale dell'heap) e -Xmx (imposta la dimensione massima dell'heap) JVM, poiché come detto, lo spazio dell'heap della generazione permanente è completamente separato dal normale spazio di Java Heap e questi argomenti sono impostati lo spazio per questo spazio heap Java regolare. Tuttavia, esistono argomenti simili che possono essere utilizzati (almeno con il jvms Sun/OpenJDK) per rendere più grande la dimensione dell'heap della generazione permanente:
-XX:MaxPermSize=128m
Il valore predefinito è 64m.
Un altro modo per prendersene cura è di consentire lo scarico delle lezioni in modo che il tuo PermGen non si esaurisca mai:
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
Cose del genere hanno funzionato magicamente per me in passato. Una cosa però, c'è un significativo scambio di prestazioni nell'usare quelli, dal momento che permgen sweeps farà come 2 richieste in più per ogni richiesta che fai o qualcosa del genere. Dovrai bilanciare il tuo uso con i compromessi.
Puoi trovare i dettagli di questo errore.
http://faisalbhagat.blogspot.com/2014/09/Java-outofmemoryerror-permgen.html
Il messaggio dello spazio Java.lang.OutOfMemoryError: PermGen
indica che l'area della memoria permanente è esaurita.
Qualsiasi applicazione Java può utilizzare una quantità limitata di memoria. L'esatta quantità di memoria che può utilizzare la tua particolare applicazione è specificata durante l'avvio dell'applicazione.
La memoria Java è separata in diverse regioni che possono essere visualizzate nell'immagine seguente:
Metaspace: nasce un nuovo spazio di memoria
JDK 8 HotSpot JVM ora utilizza la memoria nativa per la rappresentazione dei metadati di classe e si chiama Metaspace; simile a Oracle JRockit e IBM JVM.
La buona notizia è che non significa più problemi di spazio Java.lang.OutOfMemoryError: PermGen
e non è necessario regolare e monitorare più questo spazio di memoria usando Java_8_Download o più alto.
In alternativa, puoi passare a JRockit che gestisce diversamente permgen quindi jvm di Sun. In genere ha anche prestazioni migliori.
http://www.Oracle.com/technetwork/middleware/jrockit/overview/index.html
La risposta più semplice in questi giorni è usare Java 8.
Non riserva più memoria esclusivamente per lo spazio PermGen, consentendo alla memoria PermGen di interagire con il normale pool di memoria.
Tieni presente che dovrai rimuovere tutti i parametri di avvio JVM non standard -XXPermGen...=...
se non vuoi che Java 8 si lamenti di non fare nulla.
Ho avuto il problema di cui stiamo parlando, il mio scenario è Eclipse-helios + Tomcat + jsf e quello che stavi facendo è distribuire un'applicazione semplice a Tomcat. Stavo mostrando lo stesso problema qui, risolto come segue.
In Eclipse vai a server scheda doppio clic sul server registrato nel mio caso Tomcat 7.0, apre il mio file server Informazioni generali sulla registrazione. Nella sezione "Informazioni generali" fai clic sul link "Apri configurazione di avvio" , questo apre l'esecuzione delle opzioni del server nella scheda Argomenti in VM argomenti aggiunti alla fine queste due voci
-XX: MaxPermSize = 512m
-XX: PermSize = 512m
e pronto.
Nell'area di testo Opzioni Java aggiungi questa riga:
-XX:MaxPermSize=128m
L'errore di spazio perm gen si verifica a causa dell'utilizzo di uno spazio ampio piuttosto che jvm ha fornito spazio per l'esecuzione del codice. La soluzione migliore per questo problema nei sistemi operativi UNIX consiste nel modificare alcune configurazioni nel file bash. I seguenti passaggi risolvono il problema.
Esegui il comando gedit .bashrc
sul terminale.
Crea variabile Java_OTPS
con il seguente valore:
export Java_OPTS="-XX:PermSize=256m -XX:MaxPermSize=512m"
Salva il file bash. Esegui comando exec bash sul terminale. Riavvia il server.
Spero che questo approccio funzionerà sul tuo problema. Se si utilizza una versione Java inferiore a 8, questo problema si verifica a volte. Ma se usi Java 8 il problema non si verifica mai.
Aumentare la dimensione della generazione permanente o modificare i parametri del GC NON aiuterà se si ha una perdita di memoria reale. Se la tua applicazione o qualche libreria di terze parti utilizza, perde i caricatori di classe l'unica soluzione reale e permanente è trovare questa perdita e risolverla. Ci sono numerosi strumenti che possono aiutarti, uno dei più recenti è Plumbr , che ha appena rilasciato una nuova versione con le funzionalità richieste.
jrockit ha risolto anche questo per me; tuttavia, ho notato che i tempi di riavvio della servlet erano molto peggiori, quindi mentre era migliore nella produzione, era una sorta di ostacolo allo sviluppo.
Ho una combinazione di Hibernate + Eclipse RCP, ho provato ad usare -XX:MaxPermSize=512m
e -XX:PermSize=512m
e sembra funzionare per me.
Ho provato diverse risposte e l'unica cosa che finalmente ha fatto il lavoro è stata questa configurazione per il plugin del compilatore nel 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>
spero che questo aiuti.
Imposta -XX:PermSize=64m -XX:MaxPermSize=128m
. In seguito potresti anche provare ad aumentare MaxPermSize
. Spero che funzionerà. Lo stesso funziona per me. L'impostazione solo MaxPermSize
non ha funzionato per me.
Assegnare a Tomcat più memoria NON è la soluzione adeguata.
La soluzione corretta consiste nel fare una pulizia dopo che il contesto è stato distrutto e ricreato (l'implementazione a caldo). La soluzione è fermare le perdite di memoria.
Se il server Tomcat/Webapp ti sta dicendo che non è stato possibile annullare la registrazione dei driver (JDBC), quindi annullarli. Questo fermerà le perdite di memoria.
È possibile creare un ServletContextListener e configurarlo nel proprio web.xml. Ecco un esempio di 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();
}
}
}
E qui lo configuri nel tuo web.xml:
<listener>
<listener-class>
com.calculistik.mediweb.context.AppContextListener
</listener-class>
</listener>
Dicono che l'ultimo rev di Tomcat (6.0.28 o 6.0.29) gestisce l'attività di ridistribuzione dei servlet molto meglio.
Il primo passo in questo caso è verificare se il GC è autorizzato a scaricare classi da PermGen. La JVM standard è piuttosto conservativa in questo senso: le classi nascono per vivere per sempre. Quindi una volta caricate, le classi rimangono in memoria anche se nessun codice le sta usando più. Questo può diventare un problema quando l'applicazione crea molte classi dinamicamente e le classi generate non sono necessarie per periodi più lunghi. In tal caso, consentire a JVM di scaricare le definizioni di classe può essere utile. Questo può essere ottenuto aggiungendo un solo parametro di configurazione agli script di avvio:
-XX:+CMSClassUnloadingEnabled
Di default questo è impostato su false e quindi per abilitarlo è necessario impostare esplicitamente la seguente opzione nelle opzioni Java. Se abiliti CMSClassUnloadingEnabled, GC spazzerà anche PermGen e rimuoverà le classi che non vengono più utilizzate. Tieni presente che questa opzione funzionerà solo quando UseConcMarkSweepGC è abilitato anche utilizzando l'opzione sottostante. Quindi, quando esegui ParallelGC o, Dio non voglia, Serial GC, assicurati di aver impostato il tuo GC su CMS specificando:
-XX:+UseConcMarkSweepGC
Mi imbatto esattamente nello stesso problema, ma sfortunatamente nessuna delle soluzioni suggerite ha funzionato davvero per me. Il problema non si è verificato durante la distribuzione e non stavo effettuando alcuna distribuzione a caldo.
Nel mio caso il problema si è verificato ogni volta nello stesso punto durante l'esecuzione della mia web-application, durante la connessione (tramite ibernazione) al database.
Questo collegamento (anche accennato in precedenza) forniva abbastanza interni per risolvere il problema. Spostare il jdbc- (mysql) -driver dal WEB-INF e nella cartella jre/lib/ext/sembra aver risolto il problema. Questa non è la soluzione ideale, poiché l'aggiornamento a un JRE più recente richiederebbe la reinstallazione del driver. Un altro candidato che potrebbe causare problemi simili è log4j, quindi potresti voler spostare anche quello
La configurazione della memoria dipende dalla natura della tua app.
Cosa stai facendo?
Qual è l'importo delle transazioni necessarie?
Quanti dati stai caricando?
eccetera.
eccetera.
eccetera
Probabilmente potresti profilare la tua app e iniziare a pulire alcuni moduli dalla tua app.
Apparentemente questo può accadere dopo aver ridistribuito un'applicazione alcune volte
Tomcat è distribuito a caldo ma consuma memoria. Prova a riavviare il contenitore una volta ogni tanto. Inoltre, è necessario conoscere la quantità di memoria necessaria per l'esecuzione in modalità di produzione, questo sembra un buon momento per tale ricerca.
Nel caso in cui si ottenga questo nell'IDE di Eclipse, anche dopo aver impostato i parametri --launcher.XXMaxPermSize
, -XX:MaxPermSize
, etc, ancora se si ottiene lo stesso errore, molto probabilmente è che Eclipse sta usando una versione buggy di JRE che sarebbe stata installata da alcune applicazioni di terze parti e impostato su predefinito. Queste versioni buggy non raccolgono i parametri PermSize e quindi, indipendentemente da ciò che hai impostato, continuerai a ricevere questi errori di memoria. Quindi, nel tuo Eclipse.ini aggiungi i seguenti parametri:
-vm <path to the right JRE directory>/<name of javaw executable>
Assicurati inoltre di impostare il JRE predefinito nelle preferenze di Eclipse sulla versione corretta di Java.
L'unico modo che ha funzionato per me è stato con la Jock di JRockit. Ho MyEclipse 8.6.
L'heap della JVM memorizza tutti gli oggetti generati da un programma Java in esecuzione. Java utilizza l'operatore new
per creare oggetti e la memoria per i nuovi oggetti viene allocata nell'heap in fase di esecuzione. Garbage Collection è il meccanismo per liberare automaticamente la memoria contenuta dagli oggetti a cui non fa più riferimento il programma.
"Loro" sono sbagliati perché sto utilizzando 6.0.29 e ho lo stesso problema anche dopo aver impostato tutte le opzioni. Come detto in precedenza Tim Howland, queste opzioni rimandano solo l'inevitabile. Mi permettono di ridistribuire 3 volte prima di colpire l'errore invece di ridistribuire ogni volta.
Puoi anche risolvere questo problema facendo una:
rm -rf <Tomcat-dir>/work/* <Tomcat-dir>/temp/*
Deselezionando le directory e temp , Tomcat esegue un avvio pulito.
Se qualcuno sta lottando con lo stesso errore in netbeans, allora ecco come l'ho risolto.
In Netbeans:
Vai alla scheda servizi -> Direttamente sul server -> Scegli proprietà -> vai alla scheda della piattaforma -> Dentro il tipo di opzioni vm -Xms1024m
Nel mio caso, ho dato -Xms4096m
Ecco lo screenshot: