| Autor |
Mensagem |
![[Post New]](/templates/default/images/icon_minipost_new.gif) 16/07/2008 09:46:08
|
bonfarj
Java Ninja
![[Avatar]](/images/avatar/1454ca2270599546dfcd2a3700e4d2f1.jpg)
Membro desde: 28/03/2006 09:55:47
Mensagens: 298
Offline
|
O meu objetivo com este tópico é criar uma referência para solução deste problema que atinge tanta gente. Sei que existem outros tópicos sobre o assunto no fórum do GUJ, mas todos que encontrei (e não foram poucos!) tratam o problema de forma superficial. Há mais de dois anos eu procuro encontrar uma resposta definitiva para este problema sem sucesso. Consegui adiar sua solução com paliativos, aumentando o espaço destinado a memória non-heap na JVM da Sun (-XX:MaxPermSize) e posteriormente trocando esta JVM pela da BEA (JRockit). Porém, hoje cheguei um ponto que não dá mais para conviver com este problema. Espero resolvê-lo de uma vez por todas e com o meu relato aqui ajudar outras pessoas para que evitem investir tanto tempo nisto.
O sistema que desenvolvo atualmente faz uso dos seguintes frameworks/API's:
- Struts 1.2
- Hibernate 3.2
- MySQL Connector 5.0.4
- iText 2.0
- log4j 1.2.15
Obs: As outras API's são dependências dos itens dessa lista.
Completando a descrição do ambiente, este sistema roda no Tomcat 5.5.26 com Java 6 (JVM da BEA, JRockit).
Após tantos problemas com o PermGen space eu comecei a monitorar o uso dela. Após alguns dias de medição eu reparei que enquanto a memória heap oscilava bastante a memória PermGen (non-heap) apenas aumentava, nunca diminuia. Este aumento embora constante era bastante lento, porém, após operações de deploy e undeploy este aumento era considerável. A esta altura eu já sabia o tipo de objeto que era armazenado na memória non-heap, mas não conseguia entender porque estes não eram liberados após o undeploy. Boa parte das referências em português sobre o assunto falam apenas que para resolver o problema bastava aumentar a quantidade de memória, algo que todos que conviveram com isso sabem que isso apenas o adia. Uma das poucas exceções é a apresentação "Ferramentas e Técnicas para Resolução de Problemas em Desempenho" do Claudio Miranda (Summa Technologies) no JustJava 2007. Ele explica muito bem o que é cada área de memória do Java e fala sobre problemas e técnicas para solucionar problemas comuns. Graças a essa apresentação eu conheci o que acredito que seja a real causa do problema, os classloaders leaks. Pena que eu não pude assistir a apresentação, fiquei apenas com os slides, hehe. Os slides podem ser baixados aqui.
O meu entendimento sobre o problema aumentou consideravelmente após a leitura deste post no blog do Frank Kieviet. O texto é bem didático, explica de forma clara o que é um classloader leak e como ele é criado. Depois deste ele publicou outro post onde demonstra uma forma de solucionar o problema através do uso das ferramentas jmap e jhat (ambas presentes no JDK da Sun), utilizadas para gerar um dump da memória e analisá-lo, respectivamente. As ferramentas realmente são muito interessantes, no entanto, o exemplo onde ele as utilizada é bastante simples, fica fácil identificar onde ocorre o classloader leak. Em uma aplicação real, com centenas e centenas de classes, essa busca não é algo trivial, pois basta um classloader leak para que o todas as classes fiquem presas pelo classloader. Como o exemplo que classloader leak criado pelo Frank usava uma API de logging eu comecei a suspeitar dos frameworks e API's que utilizo, poderia estar fazendo mal uso deles e criando um classloader leak acidentalmente.
Buscando mais referências sobre o assunto eu acabei chegando a um relato do bug no Bugzilla do Tomcat, Tomcat 5.0.16 leaks memory when a webapp is reloaded or stopped/started, algo que tem tudo a ver com o que estou pesquisando. Segundo a descrição, bastava o deploy/undeploy de uma aplicação de exemplo do Struts (o struts-blank) extremamente simples para reproduzir o problema. Uma pessoa escreveu um comentário e falou que o problema é causado pelo uso do Java Beans Introspector pelo Struts e que o Spring resolvia isso com o uso de um listener que invoca o método Introspector.flushCaches(). Fiz um teste com o listener do Spring e outro invocando o método em um ServletContextListener que eu já utilizava, ambos sem sucesso.
Eu procurei escrever aqui tudo que sei sobre o problema até o momento. Pretendo continuar estudando os resultados do jhat, acredito que esta linha de solução seja promissora. Se alguém souber de algum problema típico no uso de alguma das API's que listei que cause classloader leaks, por favor, não deixem de falar! Eu voltarei a postar quando tiver algum progresso, espero que logo possa postar a minha solução!
Abraços a todos!
|
IGOR BRITO ALVES
@igoralves
|
|
|
 |
|
|
![[Post New]](/templates/default/images/icon_minipost_new.gif) 16/07/2008 10:20:53
|
eduveks
GUJ Ranger
![[Avatar]](/images/avatar/bce9abf229ffd7e570818476ee5d7dde.png)
Membro desde: 19/04/2005 07:45:40
Mensagens: 831
Localização: Lisboa - Portugal
Offline
|
Por acaso vc já tentou usar outro servidor? Jetty, Glassfish, etc, para ver se realmente não é um problema do Tomcat?
|
http://www.cajuscript.org
http://eduveks.blogspot.com |
|
|
 |
![[Post New]](/templates/default/images/icon_minipost_new.gif) 16/07/2008 10:37:30
|
danieldestro
Moderador
![[Avatar]](/images/avatar/a5bfc9e07964f8dddeb95fc584cd965d.png)
Membro desde: 04/09/2002 17:26:16
Mensagens: 6667
Localização: São Paulo / Catanduva
Offline
|
Trocar a JVM para versão 5 não ajuda? Tive um problema rodando Thinlet com JVM 6. Com a 5 não ocorria o erro.
This message was edited 1 time. Last update was at 16/07/2008 10:37:41
|
gotjava?
Doe sangue
What You See Is What You Get!
Apostilas de Java grátis!
RefsCALL - Bandeira Eletrônica para Árbitro de Futebol |
|
|
 |
![[Post New]](/templates/default/images/icon_minipost_new.gif) 16/07/2008 13:04:18
|
bonfarj
Java Ninja
![[Avatar]](/images/avatar/1454ca2270599546dfcd2a3700e4d2f1.jpg)
Membro desde: 28/03/2006 09:55:47
Mensagens: 298
Offline
|
eduveks wrote:Por acaso vc já tentou usar outro servidor? Jetty, Glassfish, etc, para ver se realmente não é um problema do Tomcat?
Humm, é uma boa testar isso! Eu baixei o jetty 6.1 mas não consegui fazer um "hot deploy" do sistema. Depois eu consegui fazer deploy reiniciando o jetty mas não consegui fazer o undeploy sem reiniciar, apaguei os arquivos em /contexts e /webapps mas o sistema continuava de pé. Estou procurando no Google uma forma de resolver isso, nunca havia mexido no jetty.
danieldestro wrote:Trocar a JVM para versão 5 não ajuda?
Tive um problema rodando Thinlet com JVM 6. Com a 5 não ocorria o erro.
O problema também acontecia na época que eu usava o Java 5.
Valeu pela ajuda, pessoal! Qualquer novidade estamos aí!
|
IGOR BRITO ALVES
@igoralves
|
|
|
 |
![[Post New]](/templates/default/images/icon_minipost_new.gif) 17/07/2008 09:09:59
|
bonfarj
Java Ninja
![[Avatar]](/images/avatar/1454ca2270599546dfcd2a3700e4d2f1.jpg)
Membro desde: 28/03/2006 09:55:47
Mensagens: 298
Offline
|
Eu estou confiante que a análise dos resultados do jhat vai me ajudar, mas não estou conseguindo fazer consultas com OQL. É muito estranho, tento fazer as consultas de exemplo do help mas elas não funcionam, fico esperando minutos e minutos e ele nunca responde nem mesmo as consultas mais simples.
Estou procurando uma forma de resolver isto, quando tiver uma novidade eu posto aqui. O meu objetivo é criar uma query para descobrir se há algum objeto que seja atingível por dois classloaders diferentes.
Abraços,
|
IGOR BRITO ALVES
@igoralves
|
|
|
 |
![[Post New]](/templates/default/images/icon_minipost_new.gif) 25/07/2008 09:08:31
|
newj
Thread.start()
![[Avatar]](/images/avatar/4200de485310ff951a2105ea0a9018a7.jpg)
Membro desde: 09/07/2008 07:26:37
Mensagens: 31
Offline
|
bonfarj wrote:Eu estou confiante que a análise dos resultados do jhat vai me ajudar, mas não estou conseguindo fazer consultas com OQL. É muito estranho, tento fazer as consultas de exemplo do help mas elas não funcionam, fico esperando minutos e minutos e ele nunca responde nem mesmo as consultas mais simples.
Estou procurando uma forma de resolver isto, quando tiver uma novidade eu posto aqui. O meu objetivo é criar uma query para descobrir se há algum objeto que seja atingível por dois classloaders diferentes.
Abraços,
Alguma novidade amigo?
Encontrei esse tópico aki no GUJ http://www.guj.com.br/posts/list/69165.java#363559, daí vou tentar executá-lo para ver se resolve...
Abraço,
|
The future is today! |
|
|
 |
![[Post New]](/templates/default/images/icon_minipost_new.gif) 25/07/2008 09:24:14
|
thiago.correa
GUJ Master
![[Avatar]](/images/avatar/c37f9e1283cbd4a6edfd778fc8b1c652.jpg)
Membro desde: 26/03/2006 18:54:30
Mensagens: 1861
Offline
|
Até aonde eu sei, esse problema é quando o espaço para objetos permanente estoura, uma maneira de contornar esse problema é aumentar o heap dessa memória, ou tentar reduzir o númerod de objetos estáticos. Outra coisa, vi você mencionando sobre hot deploy, no OC4J por exemplo quando faço vários deploys seguidos esse erro ocorre, daí só dando o restart, isso pode estar acontecendo o mesmo com você.
Espero ter ajudado em algo
|
---
"Se não puder ajudar, atrapalhe, afinal de contas o importante é participar!"
Thiago
 |
|
|
 |
![[Post New]](/templates/default/images/icon_minipost_new.gif) 25/07/2008 09:31:29
|
bonfarj
Java Ninja
![[Avatar]](/images/avatar/1454ca2270599546dfcd2a3700e4d2f1.jpg)
Membro desde: 28/03/2006 09:55:47
Mensagens: 298
Offline
|
Eu não pude continuar porque entrei de férias. De qualquer forma, o que foi sugerido no outro tópico não resolve o problema, apenas o adia.
Abraços,
|
IGOR BRITO ALVES
@igoralves
|
|
|
 |
![[Post New]](/templates/default/images/icon_minipost_new.gif) 20/11/2008 20:09:06
|
driwll
Smalltalk
Membro desde: 26/05/2008 08:19:46
Mensagens: 3
Offline
|
Olá pessoal.
Estou para colocar no ar uma intranet com diversas no Glassfish que terá um alto volume de acesso. Também estava com esse problema, e ontem a noite estive incessantemente atrás de uma solução, pois já estava me fazendo perder alguns fios cabelos e sono. Apos longa pesquisa pelo google, verifiquei que o problema passava pelas libs cglib e javassist e geração de bytecode na área de geração permanente da jvm, onde o garbage collector não atua.
Até então só tinha conseguido alguns parâmetros na jvm para aumentar a permgen, ou seja, simplesmente ganhar tempo, atrasando o estouro da mesma.
Depois de ler essa thread relacionada ao spring
http://forum.springframework.org/showthread.php?t=21383&page=3
e
http://my.opera.com/karmazilla/blog/2007/03/15/permgen-strikes-back
dentre muitos outros, cheguei a 3 opções:
1 - Trocar o contêiner por uma aplicação não baseada em tomcat, tais como Geronimo+Jetty ou IBM WebSphere
2 - Mudar a jvm para JRockit ou a JVM da IBM
3 - Testar a atualização do Hibernate para a versão liberada esse mês 3.3.1.GA, juntamente com javaassist 3.4GA e por precaução cglib-2.2 , bem como usar os seguintes parâmetros na JVM
-Xms512m -Xmx1024m -XX: PermSize=256m -XX:MaxPermSize=512 -XX:+UseConcMarkSweepGC -XX:+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled
A primeira opção achei não ser a mais adequada, pois já tinha uma boa quantidade de aplicações no glassfish testadas e homologadas, outra porque eu não queria perder o conjunto de recursos que este container me fornece. E também, no caso do WebSphere por questões de licenciamento.
A segunda opção também barrava em questões de licenciamento, visto que a empresa que trabalho está em fase final de licenciamento de todo software proprietário que utiliza. Seria apenas mais um custo.
Me restou a terceira opção que logicamente seria uma última esperança e, no meu ver, a mais plausível, tento em vista o "tempo de estrada" desse problema.
Bom, optei pela terceira alternativa e fiquei com muita ansiedade para constatar o resultado. Hoje pela manha cheguei no trabalho e fiz a atualização das libs e trabalhei normalmente, fazendo muitos deploys de aplicações com seam+hibernate+jsf e durante o dia todo não precisei reiniciar o Glassfish nem uma vez , considerando que sempre a cada 10 ou 15 deploys precisava reiniciar o conteiner.
Heheheheh, ao meu ver, está solucionado o problema (se ele não estiver esperando para "dar pau" amanhã).
Fica aqui minha colaboração, caso algum colega também faça a atualização dessas libs gostaria de pedir que postasse aqui a confirmação ou reprovação da sua eficacia na resolução do problema.
Um abraço a todos
|
|
|
 |
![[Post New]](/templates/default/images/icon_minipost_new.gif) 29/06/2009 10:07:50
|
gleyve
What is classpath?
Membro desde: 11/09/2008 10:56:30
Mensagens: 7
Offline
|
Não investiguei direito, mas hoje 29/06/2009 e já utilizando versões bem mais maduras das bibliotecas citadas...o problema ocorre.
De qualquer forma pretendo me aprofundar mais nisso em busca de solucionar e posto qualquer novidade.
|
|
|
 |
![[Post New]](/templates/default/images/icon_minipost_new.gif) 29/06/2009 11:00:45
|
ViniGodoy
Moderador
![[Avatar]](/images/avatar/1921493b5362e63fbe8983f4bd54157d.png)
Membro desde: 11/12/2006 08:22:01
Mensagens: 20581
Localização: Curitiba/PR
Offline
|
Não sei se você já viu, mas eu já tinha criado há algum tempo um tópico sobre o mesmo assunto:
http://www.guj.com.br/posts/list/92491.java
O netbeans possui as mesmas ferramentas do JHat, no profiler dele. Mas é milhares de vezes mais rápido e fácil de usar, especialmente se sua aplicação consome muita memória, ou tem muitas classes. Nós substituimos totalmente o uso do JHat por ele.
|
@ViniGodoy - Lattes
Tem dúvidas de Java? Poste no fórum! Não respondo dúvidas de java via MP!
Ponto V! - Desenvolvimento de Jogos Profissional - @Pontov - Facebook
Projeto Towel - Swing de uma forma inteligente (Novo lar do ObjectTableModel e do Auto-Filtro).
Ei... você está usando DefaultTableModel no seu projeto??
Não faça isso! Veja: http://www.guj.com.br/posts/list/15/199067.java#1001295 |
|
|
 |
![[Post New]](/templates/default/images/icon_minipost_new.gif) 06/07/2009 08:44:21
|
gleyve
What is classpath?
Membro desde: 11/09/2008 10:56:30
Mensagens: 7
Offline
|
Ah blz..Vou lá conferir.
Valeu!!
|
|
|
 |
![[Post New]](/templates/default/images/icon_minipost_new.gif) 30/09/2010 10:30:31
|
arllenlira
What is classpath?
![[Avatar]](/images/avatar/511bccf59c39f3a2bb87057908dc708a.jpeg)
Membro desde: 09/09/2010 15:30:17
Mensagens: 6
Offline
|
Ao inicializar a jvm aumente o valor máximo de memória usado:
passe os argumentos:
-Xms192m -Xmx768m
No Eclipse:
Window -> Preference -> Java -> Installed JRE -> Selecione o JRE -> Edit
No campo Default VM Arguments coloque o argumento
No meu caso o jboss console mostrava que 512 MB eram usado para a JVM (por default) após forçar o parâmetro ficou com 768 MB e parou o erro de "permgen".
Esse errro ocorre devido aos grande número de objetos estáticos que vão ocupando cada vez mais a memória, cada conteiner trata de forma diferente a limpeza rotineira destes objetos.
|
|
|
 |
![[Post New]](/templates/default/images/icon_minipost_new.gif) 30/09/2010 15:05:39
|
Ataxexe
JavaEvangelist
![[Avatar]](/images/avatar/8ed02495f7499c010a3b22c830438ec2.jpg)
Membro desde: 11/10/2007 15:34:17
Mensagens: 418
Localização: Brasília
Offline
|
Algumas coisas:
1 - Evite ressussitar tópicos muito antigos.
2 - Esses argumentos não tem relação com o PermGen e, sim, com a heap.
3 - Mesmo que tivessem relação (se fossem PermSize e MaxPermSize), o autor do tópico já alertou seu uso só adia o problema e que está em busca de uma solução alternativa.
4 - Não é o conteiner que trata a limpeza dos objetos, é a JVM.
|
Marcelo Guimarães
https://github.com/ataxexe
http://sourceforge.net/projects/trugger
http://www.youtube.com/user/ataxexe
http://www.flickr.com/photos/ataxexe |
|
|
 |
![[Post New]](/templates/default/images/icon_minipost_new.gif) 30/11/2010 12:25:55
|
adriano_si
JWizard
![[Avatar]](/images/avatar/4f9ef38edcfc460a00cbb8ed5dee299c.jpg)
Membro desde: 01/10/2006 15:29:40
Mensagens: 2047
Offline
|
Opa pessoal, ví que o tópico é bem antigo e por estar sendo assolado pelo problema, gostaria de compartilhar com vocês minha situação e minhas dúvidas ainda não solucionadas.
Usamos o OC4J para realizar os Deploys da aplicação, tal problema nunca tinha aparecido até o sistema entrar em Produção, onde, obviamente, o número de usuários aumentou bastante.
Quando mandei o último Deploy da aplicação, o OutOfMemoryError deu logo na entrada da aplicação... Pois bem o que ocorre é que demora algumas semanas até dá o próximo e ao parar e iniciar o container tudo volta ao normal, até porque pelo que entendi, o classloader fica vazio... Não sei o tamanho da memória em produção, até porque não temos como acessar daqui e queria pedir essa solução como um último recurso, ou seja, pra ganhar tempo caso eu precise pra de fato resolver o problema.
Minhas dúvidas quanto ao erro são os seguintes:
1 - O classloader é o espaço onde a VM vai guardando tudo o que estatico da aplicação, tipo os .class, vars static, String, contantes, etc. ???
2 - Ele enche quando se aumenta o número de acessos ou quando se faz um novo Deploy ?? porque imaginei que fosse nesse momento que os .class fossem carregados.
3 - Terei que reduzir o uso de Strings desnecessárias, isso ajudaria em algo ?? Mudar versão de Framework tbm ??
Lendo os artigos que o Viny passou nesta mesma Thread, esclareceu muita coisa mas restaram as dúvidas acima.
Uso hoje uma pancada de jars, vou fazer a pesquisa por cada versão de cada um deles e retorno se encontrar alguma coisa relacionada aos mesmo e o PermGen.
Abs []
|
"É preciso ter mais fé pra acreditar que viemos do nada..."
Blog - http://aohana.wordpress.com/
Padrão de nomenclatura Java - http://www.oracle.com/technetwork/java/codeconventions-139411.html#16712
Doc. Java - http://www.oracle.com/technetwork/java/javase/documentation/index.html
Faça perguntas Inteligentes - http://istf.com.br/perguntas
Sobrevivência no GUJ:
(Regras) http://www.guj.com.br/java/21516-regras-do-forum
(Boa prática) http://www.guj.com.br/java/15477-antes-de-voce-perguntar
(Código fonte) http://www.guj.com.br/java/50115-voce-e-novo-no-guj-vai-criar-um-topico-e-colar-seu-codigo-fonte-leia-aqui-antes-por-favor |
|
|
 |
|
|