Estou com algumas dúvidas que estão me atrapalhando bastante no que se refere a deploy/undeploy de aplicações no Tomcat
Tenho um ambiente com Tomcat 8.0.35, rodando com JDK 1.8.111. Sempre faço o deploy do arquivo WAR manualmente, pelo Manager App do Tomcat. O servidor tem 1GB de RAM e utilizamos JSF + Primefaces, Spring Security, HIbernate como framework.
Imaginem duas aplicações rodando no mesmo servidor, com esses frameworks todos… seria muita carga? Não tenho noção do consumo de RAM disso tudo, e o servidor fica no talo se eu não fizer nenhuma configuração que limite a memória da JVM.
Se eu fizer a configuração de memória limitando um pouco, já da pra ver que o sistema já não responde da mesma forma…
Outra observação: fiz o deploy de duas aplicações (uma é o núcleo do sistema, outra é uma API de integração). Se eu fazer o Undeploy da API e novamente o Deploy com uma nova versão (quero atualizar a versão API) o servidor trava. Porque? Imagino eu que não há mais memória para “criar” a nova aplicação. Dá impressão de que tudo o que o contexto da API criou, mesmo após o Undeploy, fica preso na memória da JVM. Quando faço um novo Deploy, não tem recurso disponível e tudo trava.
Mais uma observação: também já teste o seguinte => se fazer o undeploy das aplicações e clicar naquele botão do Tomcat que força o GC a rodar (Find Leaks), aparece justamente uma mensagem dizendo que há classes das aplicações XY que não podem ser “descartadas”. Parece reforçar o item 3…
Pessoal, não adquiri muito conhecimento sobre como o Java administra as memórias dele (pois sei que não tem só uma memória que armazena nossas variáveis). Creio que é isso que me falta pra solucionar esse tipo de problema.
1 - Isso é relativo. Você pode não ter nenhum framework, mas acabar tendo o heap space excedido ou um overflow.
2 - Isso com certeza. Se não me engano, o Manager App do tomcat mostra esses dados, mas não tenho certeza.
3 - Será necessário verificar o log do tomcat para ter certeza, falta de memória é uma possibilidade. Normalmente, este tipo de situação ocorre quando você abre um stream e não o encerra (qualquer tipo de leitura de arquivos, conexão a banco de dados, etc).
4 - Chegou a verificar o que estas classes fazem?
Exatamente… no meu caso eu até suspeito de uma determinada região do sistema e precisa analisar pra eliminar possíveis leaks. Mas não precisa de muito tempo/uso pro sistema pegar boa fatia de memória do servidor.
Dá pra ver lá a memória de nossas variáveis (é o Heap né?). Não dá pra ver a PermGen ou, no caso, a Metaspace (vi que no Java 8 mudou para essa nova memória)
Infelizmente, até agora, eu nunca vi um log que me dissesse OutOfMemory ou problemas com PermGen inda se aparecesse um desse eu procuraria bem em cima. Veja: estou olhando os logs da pasta do Apache mesmo…
Não consegui ainda descobrir o que está preso… se alguém tiver uma dica…
Eu nunca soube dimensionar a quantidade de memória necessária pra rodar uma aplicação, acabo sempre tendo que rodar e analisar o desempenho geral…
Uma possibilidade de reduzir o consumo de memória, caso ambos os sistemas utilizem as mesmas versões dos frameworks, é colocá-los como dependência do Tomcat (ACHO seja na pasta “lib/ext”, mas não tenho muita certeza), assim você não teria dois classloaders diferentes pra guardar as definições das mesmas classes. Não sei se o Tomcat mudou muito a hierarquia desde que eu utilizava (uns 5 anos atrás), mas ele carrega todas as classes da sua aplicação em um “classloader de aplicação”, já das “dependências confiáveis” (essas da pasta que citei anteriormente) em um classloader comum a todas as aplicações que fica acima do classloader de aplicação…
Sobre o ítem 4, essa questão de classes que não são descartadas é bem problemática quando se faz deploy e undeploy sem reiniciar o container (já li comentários de que o JDBC é um dos campeões de participação nesses problemas)… Esse problema normalmente ocorre quando o seu container não consegue descartar o classloader porque alguma das definições de classe ainda possui uma referência ativa e por isso o GC não consegue descartá-lo e continua em memória, acredito que se você puder reiniciar o Tomcat isso possa ser resolvido (só testando pra ter certeza)…
Além de tudo isso, pelo fato de não estar utilizando um application server, reiniciar o tomcat é fundamental. Se fosse um jboss da vida, bastaria derrubar o nó e subí-lo novamente (na maioria dos casos).
A questão do classloader é complexa, visto que, se ele vier a clusterizar essa solução (algum dia e nem sei se o tomcat suporte clusterização), ele precisará pensar numa estratégia, visto que, cada nó terá um classloader particular e que pode gerar problemas até para elementos singleton.
Legal Eldius! Obrigado por compartilhar esse conhecimento…
Vou sim tentar fazer testes utilizando o método de dependências providas pelo Tomcat.
Você tem razão, se eu reiniciar o Tomcat não teria problemas (realmente não tem porque quando trava eu preciso reiniciar). Pode ser que, se eu utilizar um único ClassLoader pra cada framework eu não tenha esses problemas… vamos ver. Vou testar isso e postar aqui…
Eu não pude concluir os testes que disse que ia fazer… Comecei tentando configurar o Tomcat com os JAR’s dos frameworks, porém não é só jogar os JAR’s lá.
O fato do Tomcat não ser um container com suporte a certas especificações JavaEE, tem ajustes a serem feitos no projeto que eu desconhecia e desconheço (pois não resolvi todos os erros).
Estou pensando seriamente em partir pra algum outro servidor, JBoss ou Glassfish… não sei se alguém tem alguma recomendação…
Eu uso o JBoss atualmente… Ele é legal apenas quando você precisa das “funcionalidades a mais” que ele tem (tenho essa opinião sobre todos os servidores de aplicação)… Se você não precisa usar os recursos inclusos nele, o melhor é utilizar um servlet container simples, pois necessita de menos recursos do seu servidor…
Se tiver tempo/oportunidade, tente testar o Jetty… É um pouco mais chato de instalar, pois ele não vem com um instalador, mas teve um bom desempenho quando comparei com o Tomcat, além de ter como instalar plugins (você passa alguns parâmetros para ele baixar “automagicamente” as dependências e arquivos de configuração para dar suporte funcionalidades, como CDI ou mesmo a implementação de log a ser utilizada)…
Pessoal, já se passou um bom tempo e eu não dei um parecer sobre o caso.
Embora eu não tenho conseguido realizar muitas comparações, selecionei uma das respostas que mais me abriu a mente para entender essas questões de consumo de memória, porém todas foram úteis. Agradeço a todos!