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

1222

Recentemente, encontrei este erro no meu aplicativo da web:

java.lang.OutOfMemoryError: espaço PermGen

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

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

Chris
fonte
Eu lutei contra isso por horas, mas não tenho boas notícias. Consulte minha pergunta relacionada: stackoverflow.com/questions/1996088/… Você ainda pode ter um vazamento de memória, por exemplo, as classes não são coletadas como lixo porque o seu WebAppClassLoader não é coletado como lixo (ele possui uma referência externa que não foi limpa). aumentar o PermGen atrasará apenas o OutOfMemoryError e permitir a coleta de lixo de classe é uma condição prévia, mas não coletará lixo se o carregador de classes ainda tiver referências a ele.
Eran Medan
Eu recebi este erro ao adicionar taglib de exibição . A remoção também resolveu o erro. Por quê então?
MAST
E como você se deparou com isso?
Thorbjørn Ravn Andersen
13
use JDK 1.8: þ bem-vindo ao MetaSpace
Rytek
Se estiver usando o Windows, siga estas instruções em vez de tentar definir os sinalizadores manualmente nos arquivos de configuração. Isso define corretamente os valores no registro a serem chamados pelo Tomcat durante o tempo de execução. stackoverflow.com/questions/21104340/…
MacGyver

Respostas:

563

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

-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled

Você pode fazer isso desligando o serviço tomcat, entrando no diretório Tomcat / bin e executando tomcat6w.exe. Na guia "Java", adicione os argumentos à caixa "Opções de Java". Clique em "OK" e reinicie o serviço.

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

tomcat6w //ES//servicename

onde servicename é o nome do servidor conforme exibido em services.msc

Fonte: comentário do orx no Eric's Agile Answers .

Chris
fonte
39
O artigo abaixo sugere -XX: + UseConcMarkSweepGC e -XX: MaxPermSize = 128m também. my.opera.com/karmazilla/blog/2007/03/13/…
Taylor Leese
30
-XX: + CMSPermGenSweepingEnabled Essa opção reduz o desempenho. Cada solicitação leva três vezes mais tempo do que o normal em nossos sistemas. Use com cuidado.
Eldelshell
10
trabalhei para mim - obrigado - estou fazendo isso no Ubuntu 10.10 com Tomcat6 - criei um novo arquivo: /usr/share/tomcat6/bin/setenv.sh e adicionei a seguinte linha a isso: JAVA_OPTS = "- Xms256m -Xmx512m - XX: + CMSClassUnloadingEnabled -XX: + CMSPermGenSweepingEnabled "- Reiniciou o tomcat usando: sudo /etc/init.d/tomcat6 start
sami
24
Na inicialização do tomcat 6.0.29, no meu arquivo de log catalina.out: "Use CMSClassUnloadingEnabled no lugar de CMSPermGenSweepingEnabled no futuro"
knb
222
Antes de tudo, seria ótimo explicar o que essas bandeiras realmente fazem. Apenas dizendo: "faça isso e aproveite" não é suficiente IMHO.
Nikem
251

É melhor você tentar -XX:MaxPermSize=128Mdo que -XX:MaxPermGen=128M.

Não sei dizer o uso preciso desse conjunto de memórias, mas isso tem a ver com o número de classes carregadas na JVM. (Assim, ativar o descarregamento de classe para o tomcat pode resolver o problema.) Se seus aplicativos gerarem e compilarem classes durante a execução, é mais provável que você precise de um pool de memória maior que o padrão.

toesterdahl
fonte
9
Na verdade, isso adiará apenas OOMError. Veja a resposta abaixo, iniciada por anon, com dois links para o blog frankkieviet.
Rade_303
Essas opções são explicadas aqui: oracle.com/technetwork/java/javase/tech/…
amos
153

Os erros de PermGen do servidor de aplicativos que ocorrem após várias implantações provavelmente são causados ​​por referências mantidas pelo contêiner nos carregadores de classe dos aplicativos antigos. Por exemplo, o uso de uma classe personalizada no nível do log fará com que as referências sejam mantidas pelo carregador de classes do servidor de aplicativos. Você pode detectar esses vazamentos entre carregadores de classes usando ferramentas modernas de análise JVM (JDK6 +), como jmap e jhat, para verificar quais classes continuam sendo mantidas em seu aplicativo e redesenhar ou eliminar seu uso. Os suspeitos comuns são bancos de dados, registradores e outras bibliotecas no nível da estrutura base.

Consulte Vazamentos no Classloader: a temida exceção "java.lang.OutOfMemoryError: PermGen space" e, especialmente, sua postagem de acompanhamento .

anon
fonte
3
Esta é apenas a verdadeira solução para o problema, capaz em alguns casos muito difícil de implementar.
Rade_303
Outra fonte muito boa é people.apache.org/~markt/presentations/… (do gerenciador de lançamento do Tomcat !!).
gavenkoa
22
Embora isso possa teoricamente responder à pergunta, seria preferível incluir aqui as partes essenciais da resposta e fornecer o link para referência.
Joachim Sauer
68

Os erros comuns que as pessoas cometem é pensar que o espaço de pilha e o espaço permanente são iguais, o que não é de todo verdade. Você pode ter muito espaço restante na pilha, mas ainda pode ficar sem memória em permgen.

As 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 Case Classloader, há um vazamento de memória, pois todas as classes carregadas por ele permanecerão na memória e causarão permGen fora da 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 algum vazamento de memória.
2. Aumente o tamanho do PermGen Space usando os parâmetros -XX:MaxPermSizee da JVM -XX:PermSize.

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

Peter
fonte
3
Como passar param -XX:MaxPermSize and -XX:PermSize?? Não consigo encontrar catalina.bat. Minha versão do tomcat é 5.5.26.
Deckard
Como encontrar vazamentos de memória no carregador de classes? Você recomenda alguma ferramenta?
Amit
@amit para recomendações de ferramentas, consulte a resposta do wiki da comunidade sobre esta questão.
Barett
@Deckard indo para o diretório Tomcat / bin e executando tomcat6w.exe. Na guia "Java", adicione os argumentos à caixa "Opções de Java". Clique em "OK"
Zeb
43

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

user17163
fonte
9
O único problema é que você está apenas adiando o inevitável - em algum momento você ficará sem espaço lá também. É uma ótima solução pragmática, mas não a resolve permanentemente.
Tim Howland
A mesma coisa ocorre no Eclipse e sempre que você tem muito carregamento de classe dinâmica. os carregadores de classe não são eliminados e ao vivo na geração permanente por toda a eternidade
Matt
1
Eu estava ficando sem PermGen ao executar um trabalho Hudson particularmente grande ... isso o corrigiu.
HDave 31/10/11
10
@ TimHowland, pode ser uma correção permanente se a causa raiz não for o vazamento do carregador de classes, apenas muitas classes / dados estáticos no aplicativo da web.
Péter Török
teve o mesmo problema que o HDave ao criar o jenkins / hudson a partir da fonte.
26414 louisgab
38

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

baixista
fonte
56
e se ainda persistir tente XX:MaxPermSize=1024m:)
igo
38
e se ainda persistir tente XX: MaxPermSize = 2048M :)
Thomas
16
E se ainda persistir, repense a sua aplicação !! Ou tente XX: MaxPermSize = 4096m :)
Jonathan Airey
17
você também pode tentar 8192m, mas isso é um pouco de exagero
Jacek Pietal
12
Excesso de fato - 640KB deve ser suficiente para qualquer um!
Joel Purra
28

Eu adicionei -XX: MaxPermSize = 128m (você pode experimentar o que funciona melhor) ao VM Arguments enquanto estou usando o eclipse ide. Na maior parte da JVM, o PermSize padrão é de cerca de 64 MB, que fica sem memória se houver muitas classes ou um grande número de Strings no projeto.

Para eclipse, também é descrito na resposta .

PASSO 1 : Clique duas vezes no servidor tomcat na guia Servidores

insira a descrição da imagem aqui

ETAPA 2 : Abra o Conf de inicialização e adicione -XX: MaxPermSize = 128mao final dos argumentos de VM existentes .

insira a descrição da imagem aqui

prayagupd
fonte
1
Obrigado pela sua resposta mais detalhada (Nota: clique em "Abrir configuração de inicialização" para abrir a janela "Editar configuração" ... mas usei os seguintes parâmetros: "-XX: + CMSClassUnloadingEnabled -XX: + CMSPermGenSweepingEnabled"
Chris Sim
23

Também tenho enfrentado esse problema ao implantar e desimplementar um aplicativo Web complexo, e pensei em adicionar uma explicação e minha solução.

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

Uma ou mais das classes criadas durante a vida útil do aplicativo Web mantém uma referência estática que, em algum ponto da linha, faz referência ao ClassLoader. Como a referência é originalmente estática, nenhuma quantidade de coleta de lixo limpará essa referência - o ClassLoader e todas as classes carregadas vieram para ficar.

Depois de algumas reimplantações, encontramos o OutOfMemoryError.

Agora isso se tornou um problema bastante sério. Eu poderia garantir que o Tomcat fosse reiniciado após cada reimplantação, mas isso derrubaria todo o servidor, em vez de apenas o aplicativo que está sendo reimplantado, o que geralmente não é possível.

Então, em vez disso, montei uma solução em 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 nenhum outro servidor de aplicativos .

Eu também gostaria de dizer que pessoalmente odeio esse código e que ninguém deve usá-lo como uma "solução rápida" se o código existente puder ser alterado para usar métodos adequados de desligamento e limpeza . A única vez que isso deve ser usado é se houver uma biblioteca externa da qual seu código dependa (no meu caso, era um cliente RADIUS) que não fornece um meio de limpar suas próprias referências estáticas.

Enfim, continue com o código. Isso deve ser chamado no ponto em que o aplicativo não está sendo implementado - como o método de destruição de um servlet ou (a melhor abordagem) o método contextDestroyed de um 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
fonte
Eu sei que já faz mais de 8 anos desde que escrevi isso, mas em algum momento entre agora e agora encontrei uma causa e solução raiz mais profunda. Isso ocorre enquanto todas as classes no aplicativo da web pertencem ao carregador de classes de contexto, o encadeamento que chama o retorno de chamada de inicialização e desligamento pertence ao carregador de classes pai. Isso significa que, se o código de desligamento inicializar uma variável local do encadeamento, isso fará com que o carregador de classes pai acabe mantendo uma referência ao carregador de classes de contexto, impedindo uma boa limpeza.
Edward Torbett 5/09/19
Felizmente, há uma correção muito simples - mova todo o código atualmente no método shutdown para o novo método de execução do objeto Thread. Em seguida, no método de desligamento, inicie este encadeamento e aguarde a conclusão. O código de limpeza será executado de forma idêntica, mas qualquer variável local do encadeamento permanecerá vinculada ao carregador de classes de contexto em vez de vazar.
Edward Torbett 5/09/19
20

A java.lang.OutOfMemoryError: PermGenmensagem de espaço 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, que podem ser vistas na imagem a seguir:

insira a descrição da imagem aqui

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

O JDK 8 HotSpot JVM agora está usando memória nativa para a representação dos metadados da classe e é chamado Metaspace; semelhante ao Oracle JRockit e IBM JVM.

A boa notícia é que isso significa que não há mais java.lang.OutOfMemoryError: PermGenproblemas de espaço e que você não precisa mais ajustar e monitorar esse espaço de memória usando o Java_8_Download ou superior.

Santosh Jadi
fonte
17

1) Aumentando o tamanho da memória PermGen

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

 -XX:MaxPermSize=128m

O padrão é 64m.

2) Ativar varredura

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

-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled

Coisas assim funcionaram para mim no passado. Porém, há uma troca significativa de desempenho ao usá-las, pois as permgen sweeps farão 2 pedidos extras para cada solicitação que você fizer ou algo nesse sentido. 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

faisalbhagat
fonte
Great post of @faisalbhagat faisalbhagat.blogspot.com/2014/09/…
leomeurer
A opção 2 é ótima, mas lembre-se de que não deve ser usada em ambientes de produção. Normalmente, é melhor mantê-lo apenas em ambientes de desenvolvimento. No entanto, o PermGen é removido a partir do Java 8 openjdk.java.net/jeps/122
hdost
16

Como alternativa, você pode mudar para o JRockit, que manipula permgen de maneira diferente da jvm da sun. Geralmente, também apresenta melhor desempenho.

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

Jeremy
fonte
4
Embora o JRockit realmente não tenha PermGen, isso não ajudará a longo prazo. Você vai conseguir java.lang.OutOfMemoryError: There is insufficient native memory.
Stracktracer
14

Eu tive o problema de que estamos falando aqui, meu cenário é eclipse-helios + tomcat + jsf e o que você estava fazendo é implantar um aplicativo simples no tomcat. Eu estava mostrando o mesmo problema aqui, resolvi-o da seguinte forma.

No eclipse, vá para a guia servidores, clique duas vezes no servidor registrado no meu caso, o tomcat 7.0, abre as informações gerais de registro do servidor de arquivos. 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 guia Argumentos nos argumentos da VM adicionados no final dessas duas entradas

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

E pronto.

Hugo Mendoza
fonte
14

A resposta mais simples hoje em dia é usar o Java 8.

Ele não reserva mais memória exclusivamente para o espaço PermGen, permitindo que a memória PermGen seja combinada com o conjunto de memórias regular.

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

Edwin Buck
fonte
Olá, esta resposta já foi fornecida: stackoverflow.com/a/22897121/505893 . Exclua sua resposta, para maior clareza. Se necessário, você pode melhorar a resposta que mencionei. Obrigado;)
bluish
6
@bluish Obrigado por apontar isso; no entanto, essa resposta fica de lado em falar sobre OutOfMemoryExceptions e vazamento de metadados. Também não menciona os pontos muito importantes da remoção da opção PermGen. Em suma, não tenho certeza se estaria melhorando a resposta, mas reescrevendo-a. Se fosse apenas um retoque rápido, eu me sentiria menos hesitante, mas parece que seria muito mais do que um retoque rápido, e eu odiaria ofender o autor original. Ainda assim, esta lista de respostas é uma bagunça para cães, e talvez matar o meu post seja o melhor.
Edwin Buck
8
  1. Abra o tomcat7w no 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 de Java, anexe esta linha:

    -XX:MaxPermSize=128m
  3. Defina Pool de memória inicial como 1024 (opcional).
  4. Defina o pool máximo de memória como 1024 (opcional).
  5. Clique OK.
  6. Reinicie o serviço Tomcat.
sortudos
fonte
6

O erro de espaço de geração de permissão ocorre devido ao uso de espaço grande, em vez do espaço fornecido pela jvm para executar o código.

A melhor solução para esse problema nos sistemas operacionais UNIX é alterar algumas configurações no arquivo bash. Os passos seguintes resolvem o problema.

Execute o comando gedit .bashrcno terminal.

Crie JAVA_OTPSvariável 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 essa abordagem funcione no seu problema. Se você usa uma versão Java menor que 8, esse problema ocorre algumas vezes. Mas se você usa o Java 8, o problema nunca ocorre.

Darshan
fonte
5

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 seu aplicativo ou alguma biblioteca de terceiros que ele usa, vaza carregadores de classes, a única solução real e permanente é encontrar esse vazamento e corrigi-lo. Existem várias ferramentas que podem ajudá-lo, uma das recentes é a Plumbr , que acaba de lançar uma nova versão com os recursos necessários.

Nikem
fonte
5

Além disso, se você estiver usando o log4j em seu aplicativo da web, verifique este parágrafo na documentação do log4j .

Parece que, se você estiver usando PropertyConfigurator.configureAndWatch("log4j.properties"), causará vazamento de memória ao remover o aplicativo da web.

sermojohn
fonte
4

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

Pankaj Shinde
fonte
4

Set -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 MaxPermSizenão funcionou para mim.

sandeep
fonte
4

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.

kiwilisk
fonte
"argLine" não é reconhecido pelo maven-compiler-plugin 2.4. suporta apenas "compilerArgument", que fornece o erro: <compilerArgument> -XX: MaxPermSize = 256m </compilerArgument> [ERRO] Falha na execução do javac, mas não pôde analisar o erro: javac: sinalizador inválido: -XX: MaxPermSize = 256m : javac <opções> <arquivos de origem>
Alex
Se sua fase de compilação estiver ficando sem permgen, defina o <compilerArgument> no maven-compiler-plugin. Se os testes de unidade está funcionando fora do set permgen <argLine> no maven-infalível-plugin
QWERTY
4

resolveu isso para mim também; no entanto, notei que os tempos de reinicialização do servlet eram muito piores, portanto, embora tenha sido melhor na produção, foi uma espécie de empecilho no desenvolvimento.

Tim Howland
fonte
3

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

O que você está fazendo?

Qual é a quantidade de transações pré-processadas?

Quantos dados você está carregando?

etc.

etc.

etc

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

Aparentemente, isso pode ocorrer após a reimplantação de um aplicativo algumas vezes

O Tomcat possui implantação a quente, 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; esse é um bom momento para essa pesquisa.

OscarRyz
fonte
3

Eles dizem que a versão mais recente do Tomcat (6.0.28 ou 6.0.29) lida com a tarefa de reimplementar servlets muito melhor.

Tony Ennis
fonte
3

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

No meu caso, o problema ocorreu todas as vezes no mesmo ponto durante a execução do meu aplicativo da web, enquanto eu estava conectado (via hibernação) ao banco de dados.

Esse link (também mencionado anteriormente) forneceu informações internas 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 pode causar problemas semelhantes é o log4j; portanto, você também pode mover esse

Labirinto
fonte
Se você não deseja incluir o driver no jre / lib / ext, provavelmente poderá obter os mesmos resultados incluindo o driver no caminho de classe de inicialização do contêiner. O java -cp /path/to/jdbc-mysql-driver.jar:/path/to/container/bootstrap.jar container.Instalação
Scot
3

O primeiro passo nesse caso é verificar se o GC tem permissão para descarregar classes do PermGen. A JVM padrão é bastante conservadora a esse respeito - as classes nascem para viver para sempre. Portanto, uma vez carregadas, as classes permanecem 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 definições de classe pode ser útil. Isso pode ser alcançado adicionando apenas um parâmetro de configuração aos seus scripts de inicialização:

-XX:+CMSClassUnloadingEnabled

Por padrão, isso é definido como false e, para habilitá-lo, você precisa definir explicitamente a seguinte opção nas opções Java. Se você ativar CMSClassUnloadingEnabled, o GC também varrerá o PermGen e removerá as classes que não são mais usadas. Lembre-se de que esta opção funcionará apenas quando o UseConcMarkSweepGC também estiver ativado usando a opção abaixo. Portanto, ao executar o ParallelGC ou, se Deus não permitir, o GC serial, verifique se você configurou o GC para CMS especificando:

-XX:+UseConcMarkSweepGC
sendon1982
fonte
3

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 implantação a quente). A solução é parar o vazamento de memória.

Se o servidor Tomcat / Webapp estiver informando que você não conseguiu registrar os drivers (JDBC), cancele o registro deles. Isso interromperá o vazamento de memória.

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

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

"Eles" estão errados porque estou executando o 6.0.29 e tenho o mesmo problema, mesmo depois de definir todas as opções. Como Tim Howland disse acima, essas opções apenas adiam o inevitável. Eles permitem que eu reimplante três vezes antes de atingir o erro, em vez de cada vez que reimplantar.

Ross Peoples
fonte
2

Caso você esteja obtendo isso no IDE do eclipse, mesmo depois de definir os parâmetros --launcher.XXMaxPermSize, -XX:MaxPermSizeetc, ainda assim, se estiver recebendo o mesmo erro, é mais provável que o eclipse esteja usando uma versão com erros do JRE que teria sido instalada por alguns aplicativos de terceiros e definido como padrão. Essas versões de buggy não capturam os parâmetros PermSize e, não importa o que você definir, você continuará recebendo esses erros de memória. Portanto, no seu eclipse.ini, adicione os seguintes parâmetros:

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

Certifique-se também de definir o JRE padrão nas preferências no eclipse para a versão correta do java.

Hrishikesh Kumar
fonte
2

A única maneira que funcionou para mim foi com o JRockit JVM. 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 newoperador para criar objetos, e a memória para novos objetos é alocada no heap em tempo de execução. A coleta de lixo é o mecanismo de liberar automaticamente a memória contida nos objetos que não são mais referenciados pelo programa.

user1985910
fonte
2

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

Sempre que eu tentava executar o mvn clean packagecomando, ele mostrava o seguinte erro e ocorria "BUILD FAILURE"

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 úteis 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. Procurar por <artifactId>maven-surefire-plugin</artifactId>
  3. Adicione um novo <configuration>elemento e, em seguida, <argLine>subelemento no qual passa -Xmx512m -XX:MaxPermSize=256mcomo mostrado abaixo =>

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

Espero que ajude, feliz programação :)

NIKHIL CHAURASIA
fonte