ti-enxame.com

Lidando com o erro "Java.lang.OutOfMemoryError: PermGen space"

Recentemente eu encontrei este erro na minha aplicação web:

Java.lang.OutOfMemoryError: PermGen space

É um típico aplicativo Hibernate/JPA + IceFaces/JSF em execução no Tomcat 6 e no JDK 1.6. Aparentemente isso pode ocorrer após a reimplantação de um aplicativo algumas vezes.

O que o causa e o que pode ser feito para evitá-lo? Como faço para corrigir o problema?

1206
Chris

A solução foi incluir esses sinalizadores na linha de comandos da JVM quando o Tomcat é iniciado:

-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled

Você pode fazer isso fechando o serviço Tomcat, indo para o diretório Tomcat/bin e executando o Tomcat6w.exe. Na guia "Java", adicione os argumentos à caixa "Opções Java". Clique em "OK" e reinicie o serviço.

Se você receber um erro o serviço especificado não existe como um serviço instalado você deve executar:

Tomcat6w //ES//servicename

ondeservicenameé o nome do servidor conforme exibido em services.msc

Fonte: comentário de orx em Respostas ágeis de Eric .

560
Chris

É melhor você tentar-XX:MaxPermSize=128Mem vez de -XX:MaxPermGen=128M.

Eu não posso dizer o uso preciso deste pool de memória, mas tem a ver com o número de classes carregadas na JVM. (Assim, habilitar o descarregamento de classes para o Tomcat pode resolver o problema.) Se seus aplicativos gerarem e compilarem classes na execução, é mais provável que ele precise de um conjunto de memórias maior que o padrão.

250
toesterdahl

Erros comuns que as pessoas cometem é pensar que o espaço de heap e o espaço de permgen são os mesmos, o que não é de todo verdade. Você pode ter muito espaço restante no heap, mas ainda pode ficar sem memória no permgen.

Causas comuns de OutofMemory no PermGen são ClassLoader. Sempre que uma classe é carregada na JVM, todos os seus metadados, juntamente com o Classloader, são mantidos na área PermGen e serão coletados como lixo quando o Classloader que os carregou estiver pronto para a coleta de lixo. No caso, o Classloader tem um vazamento de memória que todas as classes carregadas por ele permanecerão na memória e causarão perda de memória uma vez que você o repita algumas vezes. O exemplo clássico é Java.lang.OutOfMemoryError: PermGen Space no Tomcat .

Agora existem duas maneiras de resolver isso:
1. Encontre a causa do vazamento de memória ou se houver vazamento de memória.
2. Aumentar o tamanho do Espaço PermGen usando o parâmetro -XX:MaxPermSize e -XX:PermSize da JVM.

Você também pode verificar 2 Solution of Java.lang.OutOfMemoryError em Java para mais detalhes.

67
Peter

Use o parâmetro de linha de comando -XX:MaxPermSize=128m para uma Sun JVM (obviamente substituindo 128 pelo tamanho que você precisa).

40
user17163

Tente -XX:MaxPermSize=256m e, se persistir, tente -XX:MaxPermSize=512m

36
bassist

Eu adicionado-XX: MaxPermSize = 128m (você pode experimentar o que funciona melhor) para VM Argumentos como estou usando o Eclipse ide. Na maior parte da JVM, o padrão PermSize está em torno de 64MB , que fica sem memória se houver muitas classes ou grande número de Strings no projeto.

Para o Eclipse, também é descrito em answer .

PASSO 1 : Clique duas vezes no servidor Tomcat em Servidores Tab

enter image description here

PASSO 2 : Abrir lançamento Conf e adicionar -XX: MaxPermSize = 128m ao final do existente VM arguements .

enter image description here

26
prayagupd

Eu tenho batido minha cabeça contra esse problema ao implantar e desimplantar uma aplicação web complexa também, e pensei em adicionar uma explicação e minha solução.

Quando eu implemento um aplicativo no Apache Tomcat, um novo ClassLoader é criado para esse aplicativo. O ClassLoader é então usado para carregar todas as classes do aplicativo e, na desimplicação, tudo deve desaparecer bem. No entanto, na realidade, não é tão simples.

Uma ou mais das classes criadas durante a vida da aplicação web contém uma referência estática que, em algum lugar ao longo da linha, faz referência ao ClassLoader. Como a referência é originalmente estática, nenhuma quantidade de coleta de lixo limpará esta referência - o ClassLoader, e todas as classes carregadas, estão aqui para ficar.

E depois de algumas reimplementações, encontramos o OutOfMemoryError.

Agora isso se tornou um problema bastante sério. Eu poderia ter certeza de que o Tomcat é reiniciado após cada reimplantação, mas isso destrói todo o servidor, em vez de apenas o aplicativo ser reimplantado, o que geralmente não é viável.

Então, em vez disso, eu coloquei uma solução no código, que funciona no Apache Tomcat 6.0. Não testei em nenhum outro servidor de aplicativos e devo enfatizar que é muito provável que isso não funcione sem modificação em qualquer outro servidor de aplicativos .

Eu também gostaria de dizer que pessoalmente eu odeio esse código, e que ninguém deveria estar usando isso como uma "solução rápida" se o código existente puder ser alterado para usar os métodos apropriados de desligamento e limpeza . A única ocasião em que isso deve ser usado é se há uma biblioteca externa da qual o seu código depende (no meu caso, era um cliente RADIUS) que não fornece meios para limpar suas próprias referências estáticas.

De qualquer forma, com o código. Isso deve ser chamado no ponto em que o aplicativo está sendo desimplementado - como o método destroy de um servlet ou (a melhor abordagem) o método contextDestroyed de 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) Aumentando o tamanho da memória PermGen

A primeira coisa que se pode fazer é aumentar o tamanho do espaço de heap de geração permanente. Isso não pode ser feito com os argumentos JVM usuais –Xms (definir o tamanho inicial do heap) e –Xmx (definir o tamanho máximo do heap), já que, conforme mencionado, o espaço de heap de geração permanente é totalmente separado do espaço Java Heap regular e esses argumentos o espaço para esse espaço de heap Java comum. No entanto, existem argumentos semelhantes que podem ser usados ​​(pelo menos com o Sun/OpenJDK jvms) para aumentar o tamanho do heap de geração permanente:

 -XX:MaxPermSize=128m

O padrão é 64m.

2) Ativar a varredura

Outra maneira de cuidar disso é permitir que as classes sejam descarregadas para que seu PermGen nunca se esgote:

-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled

Coisas como essa fizeram mágica para mim no passado. No entanto, há uma diferença significativa no desempenho ao usá-los, já que varreduras de permgen farão como duas solicitações extras para cada solicitação que você fizer ou algo assim. Você precisará equilibrar seu uso com as compensações.

Você pode encontrar os detalhes desse erro.

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

15
faisalbhagat

Alternativamente, você pode alternar para o JRockit, que manipula o permgen de forma diferente do que o jvm da Sun. Geralmente tem melhor desempenho também.

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

15
Jeremy

A mensagem de espaço Java.lang.OutOfMemoryError: PermGen indica que a área da Geração Permanente na memória está esgotada.

Qualquer aplicativo Java pode usar uma quantidade limitada de memória. A quantidade exata de memória que seu aplicativo específico pode usar é especificada durante a inicialização do aplicativo.

A memória Java é separada em diferentes regiões, o que pode ser visto na imagem a seguir:

enter image description here

Metaspace: um novo espaço de memória nasce

A JVM do HotSpot do JDK 8 agora está usando memória nativa para a representação de metadados de classe e é chamada Metaspace; semelhante ao Oracle JRockit e IBM JVM.

A boa notícia é que isso significa mais problemas de espaço Java.lang.OutOfMemoryError: PermGen e não há necessidade de você ajustar e monitorar este espaço de memória usando Java_8_Download ou superior.

14
Santosh Jadi

A resposta mais simples atualmente é usar o Java 8.

Ele não mais reserva memória exclusivamente para o espaço PermGen, permitindo que a memória PermGen se misture com o pool de memória regular.

Lembre-se de que você terá que remover todos os parâmetros de inicialização da JVM -XXPermGen...=... não padrão, se não quiser que o Java 8 reclame que eles não fazem nada.

13
Edwin Buck

Eu tive o problema que estamos falando aqui, meu cenário é o Eclipse-helios + Tomcat + jsf e o que você estava fazendo é fazer um deploy de um aplicativo simples para o Tomcat. Eu estava mostrando o mesmo problema aqui, resolvido da seguinte forma.

No Eclipse, vá para servers tab clique duas vezes no servidor registrado no meu caso Tomcat 7.0, ele abre meu servidor de arquivos Informações gerais de registro. Na seção "Informações Gerais" clique no link "Abrir configuração de inicialização" , isso abre a execução das opções do servidor na aba Argumentos em VM argumentos adicionados no final destes duas entradas

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

e pronto.

13
Hugo Mendoza
  1. Abra o Tomcat7w a partir do diretório bin do Tomcat ou digite Monitor Tomcat no menu iniciar (uma janela com guias é aberta com várias informações de serviço).
  2. Na área de texto Opções Java, anexe esta linha:

    -XX:MaxPermSize=128m
    
  3. Defina o conjunto de memórias inicial como 1024 (opcional).
  4. Defina o conjunto de memória máxima como 1024 (opcional).
  5. Clique OK.
  6. Reinicie o serviço do Tomcat.
8
Lucky

Perm gen erro espaço ocorre devido ao uso de espaço grande, em vez de espaço fornecido jvm para executar o código. A melhor solução para esse problema nos sistemas operacionais UNIX é alterar algumas configurações no arquivo bash. As etapas a seguir resolvem o problema.

Execute o comando gedit .bashrc no terminal.

Crie a variável Java_OTPS com o seguinte valor:

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

Salve o arquivo bash. Execute o comando exec bash no terminal. Reinicie o servidor.

Espero que esta abordagem funcione no seu problema. Se você usar uma versão do Java inferior a 8, esse problema ocorre às vezes. Mas se você usar o Java 8, o problema nunca ocorrerá.

6
Darshan

Aumentar o tamanho da geração permanente ou ajustar os parâmetros do GC não ajudará se você tiver um vazamento de memória real. Se o seu aplicativo ou alguma biblioteca de terceiros que ele usa, vazar carregadores de classes, a única solução real e permanente é encontrar esse vazamento e consertá-lo. Há um número de ferramentas que podem ajudá-lo, uma das recentes é Plumbr , que acaba de lançar uma nova versão com os recursos necessários.

5
Nikem

Além disso, se você estiver usando log4j em seu webapp, verifique este parágrafo em log4j documentation .

Parece que, se você estiver usando PropertyConfigurator.configureAndWatch("log4j.properties"), causará vazamentos de memória quando você remover a sua webapp.

5
Yannis Sermetziadis

jrockit resolveu isso para mim também; no entanto, notei que os tempos de reinício do servlet eram muito piores, por isso, embora fosse melhor na produção, era uma espécie de empecilho no desenvolvimento.

4
Tim Howland

Eu tenho uma combinação de Hibernate + Eclipse RCP, tentei usar -XX:MaxPermSize=512m e -XX:PermSize=512m e parece estar funcionando para mim.

4
Pankaj Shinde

Eu tentei várias respostas e a única coisa que finalmente fez o trabalho foi essa configuração para o plugin do compilador no 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>

espero que este ajude.

4
kiwilisk

Definir -XX:PermSize=64m -XX:MaxPermSize=128m. Mais tarde, você também pode tentar aumentar MaxPermSize. Espero que funcione. O mesmo funciona para mim. Definir apenas MaxPermSize não funcionou para mim.

4
sandeep

Atribuir mais memória ao Tomcat NÃO é a solução adequada.

A solução correta é fazer uma limpeza após o contexto ser destruído e recriado (a implementação ativa). A solução é parar os vazamentos de memória.

Se o seu servidor Tomcat/Webapp estiver informando que falhou ao cancelar o registro de drivers (JDBC), cancele o registro deles. Isso irá parar os vazamentos de memória.

Você pode criar um ServletContextListener e configurá-lo no seu web.xml. Aqui está um exemplo de 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 aqui você configura no seu web.xml:

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

Eles dizem que a última versão do Tomcat (6.0.28 ou 6.0.29) lida com a tarefa de reimplantação de servlets muito melhor.

3
Tony Ennis

A primeira etapa nesse caso é verificar se o GC pode descarregar classes do PermGen. A JVM padrão é bastante conservadora nesse aspecto - classes nascem para viver para sempre. Então, uma vez carregadas, as classes ficam na memória mesmo que nenhum código as esteja usando. Isso pode se tornar um problema quando o aplicativo cria muitas classes dinamicamente e as classes geradas não são necessárias por períodos mais longos. Nesse caso, permitir que a JVM descarregue as definições de classe pode ser útil. Isso pode ser feito adicionando apenas um parâmetro de configuração aos seus scripts de inicialização:

-XX:+CMSClassUnloadingEnabled

Por padrão, isso é configurado como false e, para ativá-lo, é necessário definir explicitamente a opção a seguir nas opções de Java. Se você ativar CMSClassUnloadingEnabled, o GC também varrerá o PermGen e removerá as classes que não são mais usadas. Tenha em mente que esta opção funcionará somente quando o UseConcMarkSweepGC também estiver ativado usando a opção abaixo. Portanto, ao executar o ParallelGC ou, Deus não permita, o Serial GC, certifique-se de ter configurado seu GC para o CMS especificando:

-XX:+UseConcMarkSweepGC
3
sendon1982

Eu me deparo com exatamente o mesmo problema, mas infelizmente nenhuma das soluções sugeridas realmente funcionou para mim. O problema não aconteceu durante a implantação e eu não estava fazendo nenhuma implementação ativa.

No meu caso, o problema ocorreu toda vez no mesmo ponto durante a execução do meu aplicativo da web, enquanto conectava (via hibernate) ao banco de dados.

Esse link (também mencionado anteriormente) forneceu informações suficientes para resolver o problema. Mover o driver jdbc- (mysql) para fora do WEB-INF e para a pasta jre/lib/ext/parece ter resolvido o problema. Essa não é a solução ideal, pois a atualização para um JRE mais recente exigiria a reinstalação do driver. Outro candidato que poderia causar problemas semelhantes é o log4j, então você pode querer movê-lo também

3
Maze

A configuração da memória depende da natureza do seu aplicativo.

O que você está fazendo?

Qual é a quantidade de transações necessárias?

Quantos dados você está carregando?

etc.

etc.

etc

Provavelmente, você poderia criar um perfil de seu aplicativo e começar a limpar alguns módulos do seu aplicativo.

Aparentemente isso pode ocorrer após reimplantar um aplicativo algumas vezes

Tomcat tem hot deploy mas consome memória. Tente reiniciar seu contêiner de vez em quando. Além disso, você precisará saber a quantidade de memória necessária para executar no modo de produção, isso parece um bom momento para essa pesquisa.

3
OscarRyz

Caso você esteja obtendo isso no Eclipse IDE, mesmo depois de configurar os parâmetros --launcher.XXMaxPermSize, -XX:MaxPermSize, etc, ainda se você estiver recebendo o mesmo erro, é muito provável que o Eclipse esteja usando uma versão com bugs do JRE que teria sido instalada por alguns aplicativos de terceiros e definidos como padrão. Essas versões com bugs não atendem aos parâmetros do PermSize e, portanto, independentemente do que você definir, você continua recebendo esses erros de memória. Então, no seu Eclipse.ini, adicione os seguintes parâmetros:

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

Além disso, certifique-se de configurar o JRE padrão nas preferências no Eclipse para a versão correta do Java.

2
Hrishikesh Kumar

A única maneira que funcionou para mim foi com a JVM JRockit. Eu tenho o MyEclipse 8.6.

O heap da JVM armazena todos os objetos gerados por um programa Java em execução. Java usa o operador new para criar objetos e a memória para novos objetos é alocada no heap no tempo de execução. A coleta de lixo é o mecanismo de liberar automaticamente a memória contida pelos objetos que não são mais referenciados pelo programa.

2
Daniel

Eu estava tendo problema semelhante. O meu é o JDK 7 + Maven 3.0.2 + Struts 2.0 + projeto baseado em injeção de dependências GUICE do Google.

Sempre que eu tentei executarmvn clean packagecommand, ele estava mostrando o seguinte erro e "BUILD FAILURE" ocorreu

org.Apache.maven.surefire.util.SurefireReflectionException: Java.lang.reflect.InvocationTargetException; A exceção aninhada é Java.lang.reflect.InvocationTargetException: null Java.lang.reflect.InvocationTargetException Causado por: Java.lang.OutOfMemoryError: PermGen space

Eu tentei todas as dicas e truques acima, mas infelizmente nenhum funcionou para mim. O que funcionou para mim é descrito passo a passo abaixo: =>

  1. Vá para o seu pom.xml
  2. Pesquisar por <artifactId>maven-surefire-plugin</artifactId>
  3. Adicione um novo elemento <configuration> e, em seguida, <argLine> sub-elemento no qual passe -Xmx512m -XX:MaxPermSize=256m como mostrado abaixo =>

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

Espero que ajude, feliz programação :)

2
NIKHIL CHAURASIA

"Eles" estão errados porque estou executando o 6.0.29 e tenho o mesmo problema mesmo depois de configurar todas as opções. Como Tim Howland disse acima, essas opções apenas adiam o inevitável. Eles me permitem reimplantar 3 vezes antes de acertar o erro, em vez de toda vez que eu reimplanta.

2
Ross Peoples

Você também pode resolver esse problema fazendo um:

rm -rf <Tomcat-dir>/work/* <Tomcat-dir>/temp/*

Limpar os diretórios work e temp faz com que o Tomcat faça uma inicialização limpa.

1
dev

Se alguém está lutando com o mesmo erro no netbeans, então aqui está como eu o consertei.

No Netbeans:

Vá para a guia serviços -> Direto no servidor -> Escolher propriedades -> vá para a guia plataforma -> Opções de vm dentro do tipo -Xms1024m

No meu caso, eu dei -Xms4096m

Aqui está a imagem:

enter image description here

0
Satyadev