O tomcat costuma apresentar um problema chamado PermGen space Exception quando fazemos muitos undeploys e deploys sem reiniciar o processo do tomcat. Isso acontece por uma característica da jvm da Sun. Não chega a ser propriamente um bug.
Quando executamos uma aplicação java uma instância da jvm é criada exclusivamente para aquela aplicação. Na jvm existe um espaço de memória que nunca é reutilizado. Se chama espaço de memória permanente (PermGen space) e serve para guardar objetos do tipo Class. Quando se carrega uma aplicação, cada casse do jar ou war é instanciado como um objeto que fica alocado neste espaço de memória permanente. Se a aplicação tiver muitíssimas classes a jvm vai lançar uma mensagem de erro indicando que esse espaço está repleto e não é possível carregar a aplicação. Mas se as suas classes se encaixarem neste espaço (e em geral cabe) sua aplicação vai rodar sem problemas. Existem dois tipos de casos onde existe um consumo indevido do espaço de memória permanente:
- Programas que geram tipos de classe dinamicamente. O hibernate chega a criar algumas classes em tempo de execução, mas não chega a ser um número tão grande a ponto de gerar problemas. Para encher o espaço de memória permanente seria necessário criar centenas ou milhares de classes dinamicamente. Provavelmente um sistema que roda por muitos dias seguidos e criando classes novas possa chegar preencher todo este espaço. Mas seria um sistema mau escrito.
- Programas que carregam muitos tipos de classes dinamicamente. É o caso do tomcat que aceita deploy e undeploy de aplicações. Quando um desenvolvedor faz undeploy todos os objetos das classes da aplicação continuam lá no espaço de memória permanente e quando é feito o deploy da nova versão do context outros tantos objetos do tipo classe vão para a memória permanente. Em algum momento a memória acaba.
É por isso que no ambiente de desenvolvimento é comum ter de reiniciar o tomcat várias vezes ao longo do dia de trabalho. Como meu ambiente é para testes, eu limpo logs e o diretório work antes de rodar o tomcat, e coloco o tomcat.log que é o arquivo do log4j que eu configurei para exibir as mensagens do tomcat. Segue abaixo o meu script para iniciar o tomcat:
#!/bin/sh
TOMCAT_HOME="/opt/tomcat"
TOMCAT_LOGFILE="$TOMCAT_HOME/logs/tomcat.log"
cd $TOMCAT_HOME
rm logs/* -rf
rm work/Catalina/localhost/* -rf
bin/catalina.sh jpda start
ps aux|grep tomcat
echo "tomcat iniciado..."
sleep 5
while [ ! -f "$TOMCAT_LOGFILE" ]; do
sleep 1
echo "arquivo de log ainda não foi criado..."
done
echo "log do tomcat existe e será exibido a seguir:"
tail -f logs/tomcat.log
E onde está a parte de solucionar o problema do PermGen?
A forma como a Sun desenvolveu a máquina virtual java faz com que o permgen space sempre acabe nas situações descritas. Até onde eu sei a Sun não pretende mudar esta característica da arquitetura.