it-swarm.it

Gestione Dell'errore "Java.lang.OutOfMemoryError: PermGen space"

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?

1207
Chris

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 .

560
Chris

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.

250
toesterdahl

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.

67
Peter

Usa il parametro della riga di comando -XX:MaxPermSize=128m per una JVM Sun (ovviamente sostituendo 128 per qualsiasi dimensione tu abbia bisogno).

40
user17163

Prova -XX:MaxPermSize=256m e se persiste, prova -XX:MaxPermSize=512m

36
bassist

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

enter image description here

STEP 2: Apri lancio Conf e aggiungi -XX: MaxPermSize = 128m alla fine di esistente Argomenti VM .

enter image description here

26
prayagupd

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();
22
Edward Torbett

1) Aumentare le dimensioni della memoria PermGen

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.

2) Abilita spazzamento

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

15
faisalbhagat

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:

enter image description here

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.

15
Santosh Jadi

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

15
Jeremy

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.

13
Edwin Buck

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.

13
Hugo Mendoza
  1. Apri Tomcat7w dalla directory bin di Tomcat o digita Monitor Tomcat nel menu di avvio (una finestra a schede si apre con varie informazioni di servizio).
  2. Nell'area di testo Opzioni Java aggiungi questa riga:

    -XX:MaxPermSize=128m
    
  3. Impostare Initial Memory Pool su 1024 (opzionale).
  4. Imposta il massimo pool di memoria su 1024 (opzionale).
  5. Clicca Ok.
  6. Riavvia il servizio Tomcat.
8
Lucky

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.

6
Darshan

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.

5
Nikem

Inoltre se stai usando log4j nella tua webapp, controlla questo paragrafo nella documentazione di log4j .

Sembra che se si utilizza PropertyConfigurator.configureAndWatch("log4j.properties"), si causino perdite di memoria quando si annulla la distribuzione della webapp.

5

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.

4
Tim Howland

Ho una combinazione di Hibernate + Eclipse RCP, ho provato ad usare -XX:MaxPermSize=512m e -XX:PermSize=512m e sembra funzionare per me.

4
Pankaj Shinde

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.

4
kiwilisk

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.

4
sandeep

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.

3
Tony Ennis

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
3
sendon1982

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

3
Maze

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.

3
OscarRyz

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.

2
Hrishikesh Kumar

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.

2
Daniel

"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.

2
Ross Peoples

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.

1
dev

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:

enter image description here

0
Satyadev