JSF e aparente consumo de memória no servidor [resolvido]

62 respostas
A

Olá a todos!

Recentemente a equipe na qual trabalho terminou o nosso primeiro projeto feito em JSF, usando o Visual Web do Netbeans 6.1.

Quando implantamos o WAR no servidor de homologação para testar, percebemos que o consumo de memória do Tomcat 6 salta de 2% para 8%. Após usarmos o sistema durante algum tempo, o consumo de memória já encontra-se em 40%. Passando mais tempo usando o sistema, chega a 70% de memória consumida.

Pesquisamos sobre alguns parâmetros de ajuste do Garbage Collector e configuramos o serviço do Tomcat com algumas melhorias. Melhorou apenas de 1 a 2 % a menos do que antes.

Estou suspeitando que a implementação do JSF que vem no Netbeans não está removendo as referências dos objetos e, assim, o Garbage Collector não está sabendo quais objetos pode remover da memória.

Alguém já passou por isso ou tem alguma sugestão?

62 Respostas

L

Duas coisas que você deve observar…
Quais os escopos dos seus managed-beans?
Você utiliza nos seus navigations-rules?

A

Oi Lucas,

Os managed-beans que estão associados às páginas são de escopo request. É o padrão do Visual Web Netbeans.

Observei que a IDE não colocou nenhum navigation-rule como redirect.

L

Então Alexandre, geralmente a sobrecarga de memória em um servidor está relacionada a estes dois itens.
O redirect causa a constante re-instanciação dos beans do UIViewRoot atual e managed-beans com session e application ou none ligados a um desses dois.

Vou dar uma pesquisada aqui e ver se encontro alguma coisa.
Abraços

F

Oi alexandremlima,

Desculpe tirar uma dúvida com vc justamente quando é você que está querendo obter respostas para um problema seu.

É o seguinte: Estou em dúvida entre utilizar o Eclipse+richfaces (com ajax)  + implementar as páginas "na mão" e utilizar o NetBeans + Visual Web (+ ajax se possivel) + implementar as páginas com drag and drop.

Como você disse que utilizou o NetBeans + Visual Web e provavelmente utilizou o drag and drop e completou o projeto eu gostaria de saber de você se realmente vale a pena esta alternativa e quais foram os pontos negativos (se houve algum).

Espero não tirar o foco do seu problema com esta pergunta, mas vc é o primeiro que “ouvi” dizer que completou o projeto com esta alternativa.

Abraços

A

Se for para ganho de produtividade, realmente o Netbeans + Visual Web te dará isso. Porém, tome cuidado com os tutoriais na Internet sobre essa dupla. Eles ensinam a montar uma aplicação na qual sua interface conecta-se diretamente com o banco. Divida sua aplicação em camadas distintas para ganhar na manutenção e flexibilidade depois. Um ponto negativo é a falta de criar um template para as páginas - cada uma tem que ser montado desde o início.

Andei pesquisando na Internet e sempre que as pessoas usam MyFaces, Richfaces, etc, elas adicionam o Spring no meio. Eu não usei o Spring ainda, mas acho que deve aumentar a sua curva de aprendizado e afetar sua produtividade. No nosso projeto, tínhamos que ser rápidos e não obtivemos liberação para aprender nada mais do que o já tinha sido estudado pela equipe.

Estou com esse problema de consumo de memória usando o Netbeans + Visual Web. Não sei se você passaria por isso também - sendo assim um problema que acontece somente comigo - ou você enfrentaria esse problema também. Ainda não encontrei na Internet ninguém com esse mesmo problema que o meu, mas já encontrei pessoas que tiveram problemas de consumo de memória com a especificação JSF.

R

Olá alexandremlima,

O consumo de memória está realmente intenso, e a melhor maneira de descobrir o problema seria com a utilização de alguma ferramenta de profiler, assim você encontraria os gargalos na aplicação.

Pelo visto você não utiliza seus managed beans com o escopo de session, o que é um bom sinal, contudo isso não descarta a possibilidade do uso incorreto da session. Vocês estão utilizando algum componente ou framework para manter algum escopo conversacional entre as páginas?

Como está ocorrendo a paginação dos teus registros? Sob demanda ou os dados estão sendo paginados na session?

Quais os frameworks que vocês estão utilizando na aplicação?

Olá Lacerda, sinceramente eu não consigo ver o problema na utilização do redirect, poderia explicar?

M

Olá Lacerda, sinceramente eu não consigo ver o problema na utilização do redirect, poderia explicar?

Com usar redirect eu não conheço problema, o problema é pra quem não usa redirect, pois se você não redireciona o usuário após um post ele pode dar refresh ou back e mandar tudo denovo, o que não é nem um pouco interessante.

R

Olá Lacerda, sinceramente eu não consigo ver o problema na utilização do redirect, poderia explicar?

Com usar redirect eu não conheço problema, o problema é pra quem não usa redirect, pois se você não redireciona o usuário após um post ele pode dar refresh ou back e mandar tudo denovo, o que não é nem um pouco interessante.

Isso é verdade Maurício, você está certo, podemos resolver isso com o pattern PRG. Mas eu ainda não consegui ver qual a relação do redirect com o consumo de memória comentado pelo Lacerda.

A

Rponte,

A gente monitora a performance de nossa instância do Tomcat com a ferramenta Lambda Probe. Foi ela quem nos mostrou o problema de consumo de memória.

rponte:
Pelo visto você não utiliza seus managed beans com o escopo de session, o que é um bom sinal, contudo isso não descarta a possibilidade do uso incorreto da session. Vocês estão utilizando algum componente ou framework para manter algum escopo conversacional entre as páginas?

Quais os frameworks que vocês estão utilizando na aplicação?

Nós usamos apenas o que o Netbeans nos fornece, sem nenhum outro framework a não ser o plug-in Visual Web.

A paginação dos registros é feita em sessão pelo componente DataTable. Quando o problema acontece, a coleção na memória é de apenas 30 registros.

Obrigado pela atenção!

F

Oi alexandremlima,

Obrigado pela resposta sobre o Visual Web.

Em relação ao seu problema gostaria de saber como é que está o tempo de resposta do seu banco de dados. Porque se a resposta for baixa isso irá fazer com que sejam criadas mais threads no servidor causando grande uso de memória verifique também se o ciclo de vida das requisições está correto, sem deixar conexões com o banco pendentes.

[]'s

A

Nós usamos o Hibernate sob a especificação JPA para “conversar” com o banco de dados. Nós fizemos uma aplicação desktop que testa os objetos de negócio; o tempo de resposta nesse caso é bem satisfatório. A ferramenta Lambda Probe nos mostra que o Tomcat fica com mais ou menos umas 150 threads rodando enquanto o servidor estiver ativo (eu mesmo configurei o pool de threads do Tomcat). Estamos usando o Hibernate com o pool nativo do Tomcat. A ferramenta Lambda Probe mostra que as conexões estão sendo abertas e fechadas normalmente. Vou tentar pegar uns screenshoots da ferramenta Lambda Probe para postar aqui e ver se melhora o entendimento do problema.

Obrigado!

L

Alexandre, na equipe em que trabalho utilizamos o spring para gerenciamento de alguns beans, suporte ao Hibernate e sua implementação para AOP.
Realmente a curva de aprendizado é mais longa porém acredito que por tratarem-se de frameworks com papéis distintos (não utilizamos o Spring Web MVC) o tempo empreendido no assunto é de grande valia.
A produtividade aqui só aumentou depois do investimento.

rponte,
Quanto ao comentário sobre a tag , ele pode sim causar um um aumento significativo no consumo de memória se não utilizado devidamente.
No livro Java Server Faces The Complete Reference você pode encontrar toda uma explanação mais abrangente e detalhada mas a recomendação é utiliza-la somente quando existe a necessidade primordial das tuas url’s tornarem-se “Favoritas” (Bookmarks).

No processamento da tag ou de uma chamada FacesContext.getCurrentInstance().getExternalContext().redirect() você realiza um novo postback no teu servidor.
Isso acaba “forçando” ao servlet container do JSF iniciar um novo ciclo de vida quando isto não é sempre necessário.
O consumo de memória está diretamente ligado a chamada de um novo ciclo quando desnecessário.

Lembrando que logo no primeiro ciclo (Restore view) o JSF vai fazer uma nova procura aos componentes relacionados aquela nova view correspondente.
Estes componentes já haviam sido inicializados no ciclo anterior! O fato do JSF ter um delay de URL acaba confundindo pessoas com conhecimento em CGI (pessoalmente penei com isso).
Assim toda a instanciação dos componentes UI relacionados a tua UIViewRoot serão novamente chamadas.

E para piorar, como está não é uma initial view (que faz com que o JSF pule do primeiro para o último ciclo) você acaba tendo problemas com beans gerenciados por requisições.
Espero ter sido claro.

Abraços,
Gertel

A

Coletei uns screenshots de nossa instância do Tomcat, monitorada pelo Lambda Probe.

O servidor, sem nada rodando nele, está do jeito que mostram esses screenshots.






A

Após o deploy da aplicação, a memória salta para 13,9%. Forçando o Garbage Collector, consigo uma redução desse consumo.

A

Efetuei uma consulta na aplicação que retornou 35 registros e depois detalhei os dados de um deles.



A

Agora, alterei o registro, infringindo uma regra de negócio, recebi a exception, e voltei para a grid.




A

Agora, fiz outra consulta que retornou 11 registros.




A

Após tudo isso, forcei o Garbage Collector e obtive uma redução de apenas 1% na memória total consumida.

Para finalizar, vejam os screenshots que mostram o consumo de CPU no S.O. e o gráfico do comportamento da memória no Tomcat.




A

As configurações de start-up da máquina virtual para o Tomcat estão assim:
-Xms64m -Xmx256m -XX:PermSize=36m -XX:MaxPermSize=96m -XX:NewRatio=8 -Dcom.sun.management.jmxremote -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:+CMSIncrementalPacing -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled -XX:-UseGCOverheadLimit

Essa máquina que usamos para homologação das aplicações tem a segunte configuração:

  • Pentium Xeon 1.6 GHz;
  • 512 MB RAM;
  • sistema operacional Windows 2003 Server;
  • dedicada apenas a esse fim (instalado apenas o Tomcat, clientes de banco de dados e S.O.).

Espero que essas informações melhorem o entendimento do meu problema. E obrigado a todos que já se dispuseram em responder e tentar me ajudar.

A

Acrescentando mais um detalhe: quando eu removo a aplicação do Tomcat (undeploy), os objetos não estão sendo removidos da memória, nem mesmo forçando o Garbage Collector. Para a memória voltar ao normal, tenho que reinicar o serviço do Tomcat.

R

Evite o uso de session, evite qualquer tipo de paginação na session. Só uma pergunta, qual o conjunto de componentes mesmo que vocês estão utilizando? É o WoodStock?

R

Lucas Lacerda Gertel:
Alexandre, na equipe em que trabalho utilizamos o spring para gerenciamento de alguns beans, suporte ao Hibernate e sua implementação para AOP.
Realmente a curva de aprendizado é mais longa porém acredito que por tratarem-se de frameworks com papéis distintos (não utilizamos o Spring Web MVC) o tempo empreendido no assunto é de grande valia.
A produtividade aqui só aumentou depois do investimento.

rponte,
Quanto ao comentário sobre a tag , ele pode sim causar um um aumento significativo no consumo de memória se não utilizado devidamente.
No livro Java Server Faces The Complete Reference você pode encontrar toda uma explanação mais abrangente e detalhada mas a recomendação é utiliza-la somente quando existe a necessidade primordial das tuas url’s tornarem-se “Favoritas” (Bookmarks).

No processamento da tag ou de uma chamada FacesContext.getCurrentInstance().getExternalContext().redirect() você realiza um novo postback no teu servidor.
Isso acaba “forçando” ao servlet container do JSF iniciar um novo ciclo de vida quando isto não é sempre necessário.
O consumo de memória está diretamente ligado a chamada de um novo ciclo quando desnecessário.


Não há um novo postback, pois é um redirecionamento, ou seja, o cliente estará efetuando uma nova requisição a um recurso.

Na verdade, por se tratar de um redirect (ou seja, um não postback) o ciclo de vida do jsf da primeira fase pulará para a última fase. Mas sim, há uma verdade no teu comentário, redirect acaba por trazer um maior overhead no servidor devido ao número de requisições, mas ao ponto de ser algo prejudicial e o crescimento exarcebado da memória eu tenho minhas dúvidas.

R

Olá alexandremlima,

Você está utilizando o state_saving_method como “client” ou “server”?

A

Olá rponte,

Sim, estamos usando Woodstock e alguns componentes padrão. A nossa grid de registros é o componente DataTable padrão. Alguns campos do formulário são do projeto Woodstock.

Não conseguimos fazer funcionar usando escopo de request por causa do modo como o drag’n drop do Visual Web do Netbeans funcionar. Os componentes perdiam o binding constantemente.

É client.

Obrigado pela atenção!

R

alexandremlima:
Olá rponte,

Sim, estamos usando Woodstock e alguns componentes padrão. A nossa grid de registros é o componente DataTable padrão. Alguns campos do formulário são do projeto Woodstock.

Não conseguimos fazer funcionar usando escopo de request por causa do modo como o drag’n drop do Visual Web do Netbeans funcionar. Os componentes perdiam o binding constantemente.

Entendo, mas procure evitar o uso de session assim mesmo. Procure implementar uma paginação sob demanda, assim você evitará problemas de consumo de memória quando o banco crescer.

Bem, no modo client você consegue diminuir o consumo de memória no lado servidor de maneira significativa. Mas se mesmo com esta configuração ainda assim está havendo um alto consumo de memória é melhor verificar como alguns componentes do WoodStock trabalham e verificar os demais frameworks na aplicação.

Você já fez alguns testes de extress com o JMeter, por exemplo?

A

Vou tentar ver se consigo implementar a paginação com o Visual Web. A documentação da Sun (site do Netbeans) recomenda usar a sessão, e eu sempre achei estranho isso. Já estava prevendo que teríamos problemas mais tarde. Mas de qualquer modo, o Garbage Collector não deveria estar coletando essas referências de objeto quando eu destruo a sessão? O que poderia estar causando essa não-localização das referências descartáveis pelo GC? É isso que não consigo ter idéia alguma.

rponte:
Bem, no modo client você consegue diminuir o consumo de memória no lado servidor de maneira significativa. Mas se mesmo com esta configuração ainda assim está havendo um alto consumo de memória é melhor verificar como alguns componentes do WoodStock trabalham e verificar os demais frameworks na aplicação.
Você já fez alguns testes de extress com o JMeter, por exemplo?

Não sabemos usar o JMeter. Nossos testes sempre são feitos simulando o uso do usuário. Esse consumo está ocorrendo com apenas um usuári conectado ao sistema. Nem sei o que aconteceria se tivesse os dez usuários previstos simultaneamente.

Mais uma vez, obrigado!

D

Estou acompanhando o tópico e notei que os amigos que tem uma experiência maior não recomendam o uso do escopo session. Alguém pode me dizer o pq? Estamos fazendo uma aplicação e 97% dos nossos mb tem o escopo de session, do meu ponto de vista é mto mais fácil trabalhar com session do que request, mas agora me surgiu grandes dúvidas sobre o consumo desse escopo.

Alguém pode me explicar?

[]'s

A

alexandremlima:
[
Não sabemos usar o JMeter. Nossos testes sempre são feitos simulando o uso do usuário. Esse consumo está ocorrendo com apenas um usuári conectado ao sistema. Nem sei o que aconteceria se tivesse os dez usuários previstos simultaneamente.

Alexandre, de uma lida e procure na Net, pois vale a pena para voce ter ideia se sua aplicaçao ira aguentar quando chegar no mundo real.
Se esta tendo problema agora quando um usuario esta fazendo operacoes basicas, o que acontece quando forem 20.

Pelo jmeter, voce pode colocar ele para gravar passos de navegacao, ou seja, voce configura e ele escuta durante determinado periodo o que esta sendo enviado e para quem esta sendo enviado.

depois disso voce pode executar o mesmo e ele ira fazer exatamente aquilo que vc fez com a diferenca que vc pode configurar quantas threads vao rodar ao mesmo tempo.

A

afamorin, obrigado pela sua ajuda, mas uma coisa de cada vez, hehehehe. Depois que eu conseguir resolver o problema para 1 usuário, com certeza irei buscar aprender sobre o JMeter para poder simular os 10 usuários previstos da aplicação.

A

Qual a versao do TomCat que voce esta usando?

Uma coisa que voce poderia fazer para avaliar seria trocar o servidor, colocando um jetty para ver se o problema persiste e assim voce saber extamente aonde voce deve focar seu esforco em resolver o problema.

Pois matar a sessao nao quer dizer que o container vai liberar os objetos no mesmo momento.
Depende de como o mesmo foi implementado.

A

afamorim:
Qual a versao do TomCat que voce esta usando?

Uma coisa que voce poderia fazer para avaliar seria trocar o servidor, colocando um jetty para ver se o problema persiste e assim voce saber extamente aonde voce deve focar seu esforco em resolver o problema.

Pois matar a sessao nao quer dizer que o container vai liberar os objetos no mesmo momento.
Depende de como o mesmo foi implementado.

Tomcat 6.0.16 com os parâmetros de otimização que postei anteriormente.
Eu até já tinha pensado em testar com o Jetty, mas ainda teria que aprender a usá-lo. Vou tentar essa idéia depois que conseguir implementar a paginação que o rponte sugeriu. Obrigado pela sugestão!

B

Vejamos sua configuração:

  • Pentium Xeon 1.6 GHz;
  • 512 MB RAM;

Você colocar um servidor com faces para rodar numa máquina com 512MB de RAM vai ter um consumo de mémoria bem alto mesmo.

A

bobmoe:
Vejamos sua configuração:

  • Pentium Xeon 1.6 GHz;
  • 512 MB RAM;

Você colocar um servidor com faces para rodar numa máquina com 512MB de RAM vai ter um consumo de mémoria bem alto mesmo.

Essa máquina é apenas para teste de aplicação. O nosso servidor de produção é uma máquina bem mais robusta (2 processadores Pentium Xeon 1.8 GHz, 2 GB RAM, HD 400 GB).

L

Só para finalizar sobre os redirect’s.

A important consideration of when to use redirects is performance. Using a redirect will terminate the current request and cause a new request response life cycle. If the page has a very large set of compontents, this could have a noticeable performance impact. Redirects can also necessitate an extra roundtrip to re-instantiate any request-scoped objects that have dissappeared by the next request.

F

Oi alexandremlima,

Se vc ainda não achou a solução pro seu problema, aqui vão mais algumas cartas pra vc pensar rsrsrsr.

 Como vc disse que após encerrar a aplicação a memória continua com os objetos estou começando a achar que o Tomcat está com problemas pra isso vc poderia tentar uma nova instalação de uma versão mais estavel possível (que suporte a versão do jsf que vc está utilizando).

 O que se tornou suspeito pra mim é este seu analisador, ele é todo bonitinho e engraçadinho mas é confiável? É compativel com a versão do Tomcat que vc está utilizando?

  Eu indicaria utilizar recursos do próprio sistema operacional para avaliar estas informações, No JDK parece que tem um módulo chamado JConsole e o bicho é justamente pra fazer isso...análise do que está acontecendo na JVM, ou seja, vc irá se conectar com a JVM que está rodando o Tomcat e poder ver o que está rolando por lá. O ponto negativo desta idéia é ter que ralar em um tutorial pra entender as informações apresentadas.

   Se tudo isso ainda não der certo tem mais uma outra cartinha que a seguinte:

   Monte um outro projeto bem bobinho do tipo HelloWorld rsrsrsr, e vá aos poucos introduzindo os pontos interessantes do seu sistema, parta do menos crítico (quase ridículo) para o mais crítico (talvez a paginação de dados).
   A cada ponto introduzido execute a avaliação da memória para detectar qual deles que começa a bagunça toda.

   Neste teste vc não precisa utilizar o banco de dados (já que vc disse que está tudo bem com ele), vc pode montar listas fixas na mão para simular as consultas.

   Espero ter ajudado.

System.out.println(“Abraços”);

A

A versão 6.0.16 que estamos usando já é a versão estável liberada no site do Tomcat. Acredito que essa versão seja homologada para funcionar com o JSF 1.2 porque vem no Netbeans por padrão. De qualquer forma, vou testar na versão menor, 5.5.x, para ver como o Tomcat se comporta. Obrigado pela dica!

O Lambda Probe que estou usando é compatível com Tomcat e JBoss.

Fiz uma análise no servidor com o JConsole. Essa ferramenta só permite coletar os dados da instância da máquina virtual quando eu inicio o Tomcat pelo arquivo *.bat; se eu inicio o Tomcat como serviço, o JConsole não o encontra (talvez porque o processo apareça na memória como tomcat6.exe e não javaw.exe). De qualquer forma, os resultados foram semelhantes aos que o Lambda Probe me trazia.

Essa opção é a mais trabalhosa e demorada. Decidimos deixar por último, se nada mais der certo para resolver o problema. Mesmo assim, obrigado pela dica!

A

Atualizando informações sobre nossos testes…

Eu instalei uma instância do Tomcat 5.5.26 no servidor, em uma porta diferente da instância anterior. Iniciei o serviço e fiz o deploy da aplicação problemática. Deu pau! Pesquisando no Google, descobri que minha aplicação está usando JSF 1.2 que, por sua vez, depende do JSP 2.1 que, por sua vez, só está implementado no Tomcat 6. Sendo assim, uma chance foi embora.

Nossa equipe levantou algumas frentes de batalha para trabalhar as dicas que já foram dadas até aqui. Uma das pessoas está trabalhando na paginação dos registros das grids na session, eu fiquei para estudar o Jetty e testar ele, e outra pessoa ficou de ir fazendo o teste sugerido pelo fantomas, do projeto tipo HelloWorld incremental.

Mas, se alguém mais tiver como me clarear a solução para o problema, por favor, poste aqui. Obrigado a todos que contribuíram até aqui!

A

Mais informações sobre nossos testes…

Instalei o Jetty no servidor e criei um batch para iniciar o serviço com as mesmas configurações da máquina virtual que o Tomcat estava usando (ver post anterior).

Como o Lambda Probe não funciona para Jetty, segui a dica do fantomas e usei o JConsole para monitorar o serviço. Fiz exatamente o mesmo teste dos screenshots que tinha postado aqui antes. Na imagem em anexo, vocês podem ver que o consumo de memória da aplicação somente cresce e, no final onde forço o Garbage Collector pelo JConsole, cai um pouquinho mas ainda continua com o heap cheio.

Uma coisa que notei é que o Jetty realmente consome menos memória do que o Tomcat (ambos com apenas a minha aplicação no ar). Mas o problema da coleta do lixo ainda continua, indicando que não era o Tomcat que estava perturbando nossas cabeças. Ainda não aprendi ainda como fazer deploy e undeploy no Jetty sem ter que desligar e reiniciar o serviço; por isso, não sei se o Jetty remove tudo da memória quando removo o contexto da aplicação.

Os outros testes estão sendo feitos e postarei os resultados aqui. Até o momento, me parece que o culpado é realmente o JSF do plugin Visual Web do Netbeans; mas não posso provar nada ainda.


R

Cara, tente rodar a aplicacao no seu ambiente de desenvolvimento usando o profiler do netbeans e tente identificar algum memory leak. Eu faria isso antes de tentar usar o JMeter ou tentar buscar qualquer outra solucao

EDIT

Vale lembrar que se voce deve deixar sua aplicacao rodando por algum tempo no modo perfil e fazendo sempre testes nela. eh normal o uso de memoria crescer no comeco, mas chega um certo ponto que o uso de memoria deve estabilizar e so aumentar quando sao usados recursos da aplicacao. o importante do profiler eh constatar que o uso de memoria esta estabilizando num certo patamar e nao esta subindo sempre.

A

Eu rodei usando o profiler do Netbeans, mas antes tive que aprender a usá-lo em um tutorial no site da ferramenta. Objetos das classes HashMap, String e StringBuffer são a maioria nas coleções sobreviventes (surviving collections). Estes objetos estavam espalhados por todas as camadas da aplicação. Fizemos uma busca no código e fomos otimizando o uso desses objetos ao término do uso, limpando-os explicitamente e setando-os para null.

No nosso caso, a memória estabilizava quando não havia nenhuma requisição à aplicação. Sempre que uma requisição era feita, a memória crescia.

Após executar a dica do rollei, o consumo de memória da aplicação continuou o mesmo mas o garbage collector já consegue limpar em torno de 40% mais do que antes. Porém, tem uma grande parte da memória do heap que ainda não está sendo coletada. Deixamos a aplicação rodando e fizemos o uso dela durante uma hora até que o Tomcat travou e não respondeu mais. A aplicação tinha consumido todo o heap alocado. O mesmo aconteceu com o Jetty.

Mais sugestões?

R

Esse consumo exarcebado de memória está muito estranho mesmo, acreditar que o problema seja o JSF é até equivocado, pois como você mesmo disse um único usuário está fazendo tamanho estrago ao ponto de o Tomcat travar.

Verifique os teus relacionamentos nas entidades, estão utilizando fetchmode como EAGER?

Há algum framework de cache na aplicação?

Você está utilizando Spring? Tem certeza que o contexto do Spring está sendo carregado somente uma única vez?

Enfim, se realmente você acha que o problema é com o JSF, então trabalhe com ele através de mocks em algumas telas que você considera mais pesadas. Verifique se mesmo assim o consumo de memória continua alto.

Abraços.

R

alexandremlima, da uma olhada nesses objetos que nao estao sendo limpados pelo GC. olha em que classe eles estao sendo instanciados e se alguma referencia para eles nao esta ficando pendurada.

teu caso ta parecendo mesmo memory leak.

o funcionamento normal do uso da memoria seria ± assim:

comeco: uso de memoria cresce bastante ate que estabiliza em um certo ponto.
depois: toda vez que voce usa um recurso da aplicao seu uso de memoria CRESCE, mas depois DEVERIA voltar para o patamar no qual a aplicacao estabilizou

provalvelmente alguma referencia a esses objetos esta ficando pendurada e o GC nao esta podendo limpar a memoria, da uma olhada em qual objetos estao com o maior numero de geracoes e em que classes eles estao sendo criados

R

soh um exemplo de uma aplicacao e o uso de memoria dela (aplicao bem pesada por sinal, com algumas consultas que estao retornando muitos resultados)

se teu heap ta estourando eh pq o GC nao ta liberando memoria, da uma olhada ae

EDIT

independente do memory leak, se o seu programa retorna MUITOS resultados e vai ser usado por mais de um usuario ao mesmo tempo, seria bom voce colocar alguns filtros nos seus selects e limitar o numero de retornos … isso vai variar de acordo com quanta memoria voce tem sobrando

A

Primeiro, gostaria de agradecer a todos que se dispuseram a opinar e dar dicas que me pudessem solucionar o meu problema. Nós conseguimos resolver o consumo de memória exagerado da aplicação e agora o servidor não consome mais do que 10% do que foi alocado para ele (com o Garbage Collector fazendo o trabalho dele direitinho).

Estava botando a culpa no Visual Web JSF mas ele não foi o único culpado. O Hibernate também teve a sua parcela de culpa, assim como nós programadores. Coisas que fizemos que ajudaram a solucionar o problema:

  1. Identificamos, com o profiler do Netbeans, memory leaks com classes do tipo HashMap, String e StringBuffer. Então, varremos o código todo da aplicação para limpar manualmente esses objetos, ajudando o Garbage Collector. Coisas do tipo hashMap.clear(), strBuf =null e str = null quando não usávamos mais os objetos fizeram o memory leak praticamente desaparecer de nossa aplicação.

  2. Identificamos que o Hibernate estava usando o EhCache com pior performance, a default. Entramos no site do EhCache para aprender a configurar o second level cache do Hibernate corretamente.

  3. Identificamos que o pool C3P0 do Hibernate estava prendendo threads no Tomcat devido a configuração default. Entramos no site do C3P0 e aprendemos a configurar corretamente o pool, com várias otimizações de performance indicadas na documentação.

  4. Como os componentes Woodstock só conseguem manter o binding ativo em escopo de sessão, fizemos rotinas que são chamadas em várias ações do usuário para limpar as variáveis da sessão constantemente quando não estão mais em uso.

Ficam aí as dicas para a galera:

  • as configurações default do Hibernate são uma bosta! Se não quiser ter problemas, consulte a documentação mais a fundo para fazer o tuning do cache e do pool.
  • o visual web jsf do NetBeans é um consumidor voraz de sessão, tome cuidado com o seu uso.

Sou muito grato a todos! Bye!

D

Sem querer ser chato, mas sendo, poderia postar os links das soluções, ou os que dispuser?
Seria interessante pra mim, pelo menos, não perder tempo garimpando por ai até achar o “centro” do negócio.

Obrigado

A

Claro!

Hibernate e C3P0 - http://www.hibernate.org/214.html
C3P0 - http://www.mchange.com/projects/c3p0/
Hibernate e EhCache - http://ehcache.sourceforge.net/documentation/hibernate.html

Amanhã, no trabalho, eu pego alguns trechos de código e posto aqui.

F

Oi alexandremlima,

Parabéns a vc e toda sua equipe! :smiley:

Gostei bastante da sua atitude de colocar os resultados do seu trabalho no forum.

Um grande abraço e boa sorte em seus novos projetos.

A

alexandremlima:

  1. Identificamos, com o profiler do Netbeans, memory leaks com classes do tipo HashMap, String e StringBuffer. Então, varremos o código todo da aplicação para limpar manualmente esses objetos, ajudando o Garbage Collector. Coisas do tipo hashMap.clear(), strBuf =null e str = null quando não usávamos mais os objetos fizeram o memory leak praticamente desaparecer de nossa aplicação.

Para resolver o memory leak, fizemos várias coisas desse tipo em nosso código:

// ...
StringBuffer sqlBuffer = new StringBuffer("select u from Usuario u");
// ...
Query qry = entityManager.createQuery(sqlBuffer.toString());
// ...
sqlBuffer = null;
qry = null;
// ...
// ...
Map filtro = new HashMap();
// ...
filtro.clear();
filtro = null;
// ...
// ...
Facade facade = new Facade();
Usuario usuario = facade.obterUsuarioPorLoginSenha(usuarioResq.getLoginUsuario(), senhaCripto);
// ...
usuario = null;
facade = null;
// ...
A

alexandremlima:

2) Identificamos que o Hibernate estava usando o EhCache com pior performance, a default. Entramos no site do EhCache para aprender a configurar o second level cache do Hibernate corretamente.

No persistence.xml, colocamos essas linhas:

<!-- ... -->
<property name="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider"/>
<property name="hibernate.cache.use_structured_entries" value="true"/>
<!-- ... -->

Criamos um arquivo ehcache.xml no classpath da aplicação com o conteúdo que configurava o cache do Hibernate para os objetos que queríamos que ficassem em cache:

<ehcache>

    <diskStore path="java.io.tmpdir"/>
    
    <defaultCache
        maxElementsInMemory="100"
        eternal="false"
        timeToIdleSeconds="60"
        timeToLiveSeconds="120"
        overflowToDisk="false"
        />

    <cache name="embasa.copa.model.MotivoUrgencia"
        maxElementsInMemory="20"
        eternal="false"
        timeToIdleSeconds="30"
        timeToLiveSeconds="60"
        overflowToDisk="false"
        />

    <cache name="embasa.copa.model.MotivoInviabilidade"
        maxElementsInMemory="20"
        eternal="false"
        timeToIdleSeconds="30"
        timeToLiveSeconds="60"
        overflowToDisk="false"
        />

    <cache name="embasa.copa.model.Prioridade"
        maxElementsInMemory="10"
        eternal="false"
        timeToIdleSeconds="30"
        timeToLiveSeconds="60"
        overflowToDisk="false"
        />
        
    <cache name="embasa.copa.model.Status"
        maxElementsInMemory="10"
        eternal="false"
        timeToIdleSeconds="30"
        timeToLiveSeconds="60"
        overflowToDisk="false"
        />

</ehcache>

E, nas entidades que ficariam em cache, colocamos essa anotação Cache:

// ...
@Entity
@Table(name = "motivourgencia", schema = "public")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
public class MotivoUrgencia implements Serializable {
// ...
A

alexandremlima:

3) Identificamos que o pool C3P0 do Hibernate estava prendendo threads no Tomcat devido a configuração default. Entramos no site do C3P0 e aprendemos a configurar corretamente o pool, com várias otimizações de performance indicadas na documentação.

Para ativar o pool do C3P0, basta colocar as seguintes tags no persistence.xml:

<!-- ... -->
<property name="hibernate.c3p0.min_size" value="0"/>
<property name="hibernate.c3p0.max_size" value="6"/>
<property name="hibernate.c3p0.timeout" value="60"/>
<property name="hibernate.c3p0.max_statements" value="0"/> <!-- preste atenção aqui -->
<property name="hibernate.c3p0.acquire_increment" value="1"/>
<property name="hibernate.c3p0.idle_test_period" value="60"/>
<!-- ... -->

A propriedade max_statements tem que ficar com o valor zero, segundo um dos posts em fóruns que encontrei, devido a um bug do Hibernate Entity Manager 3.

Criamos um arquivo c3p0.properties no classpath da aplicação com o seguinte conteúdo:

# sobrescrevendo a configuração do persistence.xml
c3p0.minPoolSize=0
c3p0.maxPoolSize=6
c3p0.maxIdleTime=60
c3p0.maxStatements=0
c3p0.acquireIncrement=1
c3p0.idleConnectionTestPeriod=60

# configurações específicas do c3p0
c3p0.checkoutTimeout=60
c3p0.initialPoolSize=0
c3p0.maxStatementsPerConnection=10
c3p0.acquireRetryDelay=1000
c3p0.acquireRetryAttempts=60
c3p0.breakAfterAcquireFailure=false
c3p0.maxIdleTimeExcessConnections=60
c3p0.numHelperThreads=3

c3p0.debugUnreturnedConnectionStackTraces=true
c3p0.unreturnedConnectionTimeout=60

Com essa configuração, o C3P0 parou de prender as threads no Tomcat e passou a trabalhar corretamente, limpando o pool periodicamente e forçando o fechamento de conexões que possam ter ficado presas por algum motivo e que não estejam mais em uso.

A

alexandremlima:

4) Como os componentes Woodstock só conseguem manter o binding ativo em escopo de sessão, fizemos rotinas que são chamadas em várias ações do usuário para limpar as variáveis da sessão constantemente quando não estão mais em uso.

Criamos um método que limpa o que a tela usou da sessão, deixando a sessão do mesmo jeito que estava antes de entrar na tela. Assim, se o usuário apertar no botão Cadastrar, por exemplo, no final do método que processa essa requisição nós chamamos o método que limpa a sessão. Se o usuário não clicar em Cadastrar e for para outro lugar da aplicação, nós também invocamos o método que limpa a sessão.

A

Finalmente, reparem nesse screenshot da ferramenta Lambda Probe que mostra como ficou a memória do Tomcat depois de a gente ter usado exaustivamente a aplicação.

D

Muito obrigado mesmo. Essas informações são mais valiosas do que imagina. Tem muita gente que não sabe o que ocorre de errado, eu sou uma delas, porque investigar a fundo leva tempo e muitas vezes é justamente o que não dispomos.
Vai para os meus favoritos.

D

Só esqueci de dar os sinceros parabéns pelo feito a você e sua equipe. Muito bom. Já comecei a fazer os ajustes finos em uma aplicação que fizemos aqui usando o Visual Web JSF.
Mas só uma coisa, esse código de finalizar a sessão é para destruí-la ou fizeram algo mais?

Grato

R

editado 8)

A

djemacao:
Só esqueci de dar os sinceros parabéns pelo feito a você e sua equipe. Muito bom. Já comecei a fazer os ajustes finos em uma aplicação que fizemos aqui usando o Visual Web JSF.
Mas só uma coisa, esse código de finalizar a sessão é para destruí-la ou fizeram algo mais?

Obrigado pelas congratulações!

O código é para limpar o que tinha sido usado pela página - e que foi posto na sessão - quando o usuário sai dela. Também, quando entra na página, o código limpa a sessão e a deixa “zerada” para o caso de uso da página.

R

Olá alexandremlima,

alexandremlima:
Obrigado pelas congratulações!

O código é para limpar o que tinha sido usado pela página - e que foi posto na sessão - quando o usuário sai dela. Também, quando entra na página, o código limpa a sessão e a deixa “zerada” para o caso de uso da página.

Mesmo que sua atual solução para limpar a session esteja funcionando, eu te aconselho a trabalhar com algum componente ou framework para controle conversacional dos managed beans. O componente t:saveState do Tomahawk é uma boa opção, ele trabalha num escopo mais longo que request e mais curto que session, ao mudar para uma página sem referência do componente os dados são removidos automaticamente da memória, vale a pena você fazer alguns testes.

Abraços.

A

rponte:
Olá alexandremlima,

Mesmo que sua atual solução para limpar a session esteja funcionando, eu te aconselho a trabalhar com algum componente ou framework para controle conversacional dos managed beans. O componente t:saveState do Tomahawk é uma boa opção, ele trabalha num escopo mais longo que request e mais curto que session, ao mudar para uma página sem referência do componente os dados são removidos automaticamente da memória, vale a pena você fazer alguns testes.

Abraços.

Usaria ele se o plugin Visual Web do Netbeans 6.1 me permitisse usar outros frameworks (MyFaces, por exemplo). Não descobri ainda como usar componentes de outros frameworks junto com os componentes do Woodstock. Se você souber como, por favor, me ensine! :smiley:

R
c3p0.checkoutTimeout=60

revivendo o topico soh para fazer uma observacao. esse teu checkoutTimeout nao ta muito pequeno nao? Essa propriedado do c3p0 eh em milisegundos, se por algum atraso na rede voce nao conseguir uma conexao nesse tempo ele vai dar um timeout.

M

Segui esse tutorial mas continuo tendo estouro de memória, ja coloquei so uma classe em cache com um objeto somente, expirando em 10 segundos, mas a memória continua subindo até acabar.
Estou usando Struts 2, Hibernate, c3po e ehCache.
Alguem tem una ideia de como diminir isso?

M

No servidor so tenho 128 MB de ram para a minha VM.
se fico recarregando a mesma pagina varias vezes para testar a memória vai subindo e subindo até acabar os 128 MB dai a mensagem java.lang.OutOfMemoryError: Java heap space , é muito estranho a mesma tela recarregada ir acessando mais e mais memória. alguem ja passou por isso?

I

Seguir o tutorial e funciocou certinho, coloquei o lazy true na aplicação, mas para isso tive que criar um filtro.
Ajudei o GC com colocando null.
Mas surgiu um outro problema, quando a aplicação fica parada a memória começa a subir o comsumo, alguem saberia me explicar pq e como arrumar?
abraços a todos

Criado 11 de julho de 2008
Ultima resposta 14 de out. de 2009
Respostas 62
Participantes 12