Ajuda - Problema de Memory Leak

Bom dia galera,

Já faz algum tempo que estou lutando contra um memory leak em uma aplicação,
mas apesar de já ter conseguido alguns avanços, ainda tenho problemas.

Antes de mais nada, algumas informações sobre a aplicação e o ambiente:

  • Framework WEB: JSF (Woodstock 2.4)
  • Framework ORM: JPA (Hibernate - os jars presentes no Netbeans)
  • Sem EJB
  • Servidor: JBoss 5.0.1
  • SO: SunOS 5.10
  • Arquitetura so servidor: sparcv9 (64bits)
  • Processadores: 32 (8 chips quadcore)
  • Memória física disponível: 8GB
  • Configurações da JVM:

-Xmx3800m -Xms3800m -Xmn2g -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC
-XX:+UseParNewGC -XX:SurvivorRatio=6 -XX:TargetSurvivorRatio=90
-XX:MaxTenuringThreshold=31 -Dorg.jboss.resolver.warning=true
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
-Djava.awt.headless=true -Dsun.rmi.dgc.client.gcInterval=3600000
-Dsun.rmi.dgc.server.gcInterval=3600000

Acredito que com a configuração do servidor de aplicações não há nada de errado, pois os tempos gastos
em GC são baixos, e como visto, a máquina é ótima.

Histórico:

Antes, quando percebemos os primeiros problemas de memória, nunca havia a remoção de objetos
da Old Gen, e esta se enchia rapidamente,em 2 dias, digamos.
Mas conseguimos identificar um problema com EntityManagerFactories
que ficavam abertos. Depois de resolver este problema a Old Gen é inspecionada durante o
GC e alguns objetos são removidos, sendo que o tempo para que ela se encha, agora, é maior.

O principal sintoma deste memory leak que ainda persiste é que a cada dia uma quantidade de memória
fica presa na Old Gen e nunca é removida, até que ela se enche e a aplicação se arrasta
até travar.

Gostaria de saber se alguém já teve um problema parecido, e àqueles que manjam
das entranhas do Hibernate, gostaria de alguma ajuda também (pois tenho uma
suspeita de que ele possa estar envolvido).

Eis uma lista das 50 primeiras classes cujas instâncias ocupam mais memória no
meu servidor, num determinado momento. No momento deste relatório, feito com o
Jmap, minha Old Gen estava com 1.6 GB acumulados (somente ela, praticamente). No
Eden havia pouco mais de 100 MB no momento.

Size (MB) |--------| Count |--------| Class description

575,45 |--------| 3383654 |--------| char[]
190,4 |--------| 6019593 |--------| java.lang.String[]
132,62 |--------| 3476627 |--------| java.lang.String
120,96 |--------| 2642485 |--------| java.util.HashMap$Entry
114,88 |--------| 559779 |--------| int[]
88,59 |--------| 480548 |--------| java.util.HashMap$Entry[]
58,26 |--------| 477241 |--------| java.lang.reflect.Field
41,36 |--------| 320104 |--------| java.lang.String[][]
28,93 |--------| 473956 |--------| java.util.HashMap
25,69 |--------| 794474 |--------| boolean[]
25,39 |--------| 171271 |--------| * ConstMethodKlass
18,3 |--------| 16308 |--------| * ConstantPoolKlass
17,83 |--------| 187164 |--------| java.lang.Object[]
17 |--------| 171271 |--------| * MethodKlass
13,24 |--------| 15496 |--------| org.hibernate.persister.entity.SingleTableEntityPersister
12,7 |--------| 240176 |--------| * SymbolKlass
12,41 |--------| 77480 |--------| org.hibernate.loader.entity.EntityLoader
12,32 |--------| 179363 |--------| org.hibernate.loader.DefaultEntityAliases
11,65 |--------| 190840 |--------| org.hibernate.tuple.StandardProperty
11,63 |--------| 14336 |--------| * ConstantPoolCacheKlass
11,52 |--------| 503479 |--------| java.lang.Integer
11,22 |--------| 16308 |--------| * InstanceKlassKlass
10,63 |--------| 174188 |--------| java.util.TreeMap$Entry
10,25 |--------| 18928 |--------| org.hibernate.persister.collection.OneToManyPersister
8,61 |--------| 225680 |--------| org.hibernate.property.Dom4jAccessor$ElementGetter
8,54 |--------| 223912 |--------| org.hibernate.property.DirectPropertyAccessor$DirectGetter
8,54 |--------| 223912 |--------| org.hibernate.property.DirectPropertyAccessor$DirectSetter
8,44 |--------| 58200 |--------| java.lang.reflect.Method
7,39 |--------| 28042 |--------| byte[]
6,89 |--------| 225680 |--------| org.hibernate.property.Dom4jAccessor$ElementSetter
6,34 |--------| 66768 |--------| org.hibernate.property.Setter[]
6,34 |--------| 66768 |--------| org.hibernate.property.Getter[]
5,97 |--------| 111800 |--------| org.hibernate.type.ManyToOneType
5,39 |--------| 117807 |--------| java.util.Hashtable$Entry
5,19 |--------| 8192 |--------| * MethodDataKlass
5,17 |--------| 225680 |--------| org.hibernate.property.MapAccessor$MapGetter
5,17 |--------| 225680 |--------| org.hibernate.property.MapAccessor$MapSetter
4,73 |--------| 30992 |--------| org.hibernate.loader.entity.CascadeEntityLoader
4,71 |--------| 62920 |--------| org.hibernate.engine.CascadeStyle[]
4,32 |--------| 129066 |--------| org.hibernate.LockMode[]
4,32 |--------| 129066 |--------| org.hibernate.loader.EntityAliases[]
4,32 |--------| 129065 |--------| org.hibernate.type.EntityType[]
4,28 |--------| 127816 |--------| org.hibernate.persister.entity.Loadable[]
4,14 |--------| 36177 |--------| java.lang.reflect.Constructor
4,1 |--------| 67145 |--------| javax.servlet.jsp.tagext.TagAttributeInfo
3,94 |--------| 37864 |--------| org.hibernate.type.Type[]
3,87 |--------| 84554 |--------| antlr.ANTLRHashString
3,85 |--------| 38830 |--------| org.jboss.virtual.plugins.context.zip.ZipEntryHandler
3,67 |--------| 96200 |--------| org.hibernate.type.BagType
3,65 |--------| 31077 |--------| int[][]
3,64 |--------| 79503 |--------| java.util.concurrent.ConcurrentHashMap$HashEntry

Há alguns dias fiz um relatório, recolhendo este tipo de informação por várias vezes em 2 dias.
Neste relatório eu comparei principalmente os objetos do Hibernate.
Durante a realização deste relatório, reiniciei o servidor num momento e, com a garantia de que
ninguém estava logado, recolhi as quantidades “iniciais” desses objetos.

Abaixo coloco um comparativo das quantidades iniciais para os objetos do Hibernate acima listados.
Coloco também as quantidades da amostra acima, apenas para comparação:

Inicial |--------| Atual |--------| Class description

149 |--------| 15496 |--------| org.hibernate.persister.entity.SingleTableEntityPersister
745 |--------| 77480 |--------| org.hibernate.loader.entity.EntityLoader
1723 |--------| 179363 |--------| org.hibernate.loader.DefaultEntityAliases
2021 |--------| 190840 |--------| org.hibernate.tuple.StandardProperty
182 |--------| 18928 |--------| org.hibernate.persister.collection.OneToManyPersister
2170 |--------| 225680 |--------| org.hibernate.property.Dom4jAccessor$ElementGetter
4200 |--------| 223912 |--------| org.hibernate.property.DirectPropertyAccessor$DirectGetter
3988 |--------| 223912 |--------| org.hibernate.property.DirectPropertyAccessor$DirectSetter
21700 |--------| 225680 |--------| org.hibernate.property.Dom4jAccessor$ElementSetter
642 |--------| 66768 |--------| org.hibernate.property.Setter[]
642 |--------| 66768 |--------| org.hibernate.property.Getter[]
1827 |--------| 111800 |--------| org.hibernate.type.ManyToOneType
21700 |--------| 225680 |--------| org.hibernate.property.MapAccessor$MapGetter
21700 |--------| 225680 |--------| org.hibernate.property.MapAccessor$MapSetter
298 |--------| 30992 |--------| org.hibernate.loader.entity.CascadeEntityLoader
605 |--------| 62920 |--------| org.hibernate.engine.CascadeStyle[]
1239 |--------| 129066 |--------| org.hibernate.LockMode[]
1239 |--------| 129066 |--------| org.hibernate.loader.EntityAliases[]
1239 |--------| 129065 |--------| org.hibernate.type.EntityType[]
1229 |--------| 127816 |--------| org.hibernate.persister.entity.Loadable[]
363 |--------| 37864 |--------| org.hibernate.type.Type[]
1295 |--------| 96200 |--------| org.hibernate.type.BagType

Bem, essas são apenas algumas das classes.
Como disse, tenho apenas a suspeita (pra mim com fortes indícios) de que
há algum problema envolvendo o Hibernate. Veja, não estou dizendo que o prolema É O
Hibernate, mas algo o envolvendo. Eu, na minha leiga opinião, acredito que parte dos
outros objetos que também ocupam bastante memória (arrays de tipos primitivos) estão
de certa forma relacionados a estes objetos do Hibernate.

A situação é esta galera. Eu realmente não sei mais pra quem apelar, a não ser a
comunidade.
Apesar de algumas ferramentas, como o Jmap, VisualVM e JConsole ajudarem na visualização
e comprovação do problema, não ajudam muito na identificação da causa.
Uma ferramenta que eu vi que poderia ajudar na identificação é o JBoss Profiler,
mas em sua atual versão ele não conta com um ambiente gráfico que possa mostrar os
relacionamentos entre os objetos, dentre outras análises que o mesmo realizava em uma versão
anterior.

Qualquer ajuda é bem vinda, conto com a ajuda de vocês!

Abraços!