Pergunta

Um de nossos programas às vezes está recebendo um OutOfMemory erro na máquina de um usuário, mas é claro que não quando estou testando.Acabei de executá-lo com JProfiler (em uma licença de avaliação de 10 dias porque nunca usei antes) e filtrando nosso prefixo de código, a maior parte em tamanho total e número de instâncias é mais de 8.000 instâncias de uma classe simples específica .

Cliquei no botão "Coleta de lixo" no JProfiler, e a maioria das instâncias de outras classes nossas desapareceram, mas não essas em particular.Executei o teste novamente, ainda na mesma instância, e ele criou mais 4.000 instâncias da classe, mas quando cliquei em "Coleta de lixo", elas desapareceram, deixando as mais de 8.000 originais.

Essas instâncias ficam presas em várias coleções em vários estágios.Presumo que o fato de eles não serem coletados como lixo deve significar que algo está mantendo uma referência a uma das coleções, de modo que está mantendo uma referência aos objetos.

Alguma sugestão de como posso descobrir o que está segurando a referência?Estou procurando sugestões sobre o que procurar no código, bem como maneiras de descobrir isso no JProfiler, se houver.

Foi útil?

Solução

Despeje a pilha e inspecione -o.

Tenho certeza de que há mais de uma maneira de fazer isso, mas aqui está simples. Essa descrição é para o MS Windows, mas etapas semelhantes podem ser tomadas em outros sistemas operacionais.

  1. Instale o JDK se você ainda não o possui. Ele vem com um monte de ferramentas legais.
  2. Inicie o aplicativo.
  3. Abra o gerenciador de tarefas e encontre o ID do processo (PID) para java.exe (ou qualquer executável que você esteja usando). Se os PIDs não forem mostrados por padrão, use View> Selecione colunas ... para adicioná -las.
  4. Despeje o heap usando JMAP.
  5. Comece o Jhat servidor no arquivo que você gerou e abra seu navegador para http: // localhost: 7000 (A porta padrão é 7000). Agora você pode navegar no tipo em que está interessado e informações como o número de instâncias, o que tem referências a eles, etc.

Aqui está um exemplo:

C:\dump>jmap -dump:format=b,file=heap.bin 3552

C:\dump>jhat heap.bin
Reading from heap.bin...
Dump file created Tue Sep 30 19:46:23 BST 2008
Snapshot read, resolving...
Resolving 35484 objects...
Chasing references, expect 7 dots.......
Eliminating duplicate references.......
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.

Para interpretar isso, é útil entender alguns dos Nomenclatura do tipo de matriz Java usa - como saber disso classe [ljava.lang.Object; realmente significa um objeto do tipo Objeto[.

Outras dicas

Experimente o Eclipse Memory Analyzer. Ele mostrará para cada objeto como ele está conectado a uma raiz GC - um objeto que não é coletado de lixo porque é mantido pela JVM.

Ver http://dev.eclipse.org/blogs/memoryanalyzer/2008/05/27/automated-wep-dump-analysis-finding-memory-leaks-with-one-click/ Para obter mais informações sobre como funciona o MAT Eclipse.

Eu olhava para as coleções (especialmente as estáticas) em suas aulas (os hashmaps são um bom lugar para começar). Veja este código, por exemplo:

Map<String, Object> map = new HashMap<String, Object>(); // 1 Object
String name = "test";             // 2 Objects
Object o = new Object();          // 3 Objects
map.put(name, o);                 // 3 Objects, 2 of which have 2 references to them

o = null;                         // The objects are still being
name = null;                      // referenced by the HashMap and won't be GC'd

System.gc();                      // Nothing is deleted.

Object test = map.get("test");    // Returns o
test = null;

map.remove("test");               // Now we're down to just the HashMap in memory
                                  // o, name and test can all be GC'd

Enquanto o hashmap ou outra coleção tiver uma referência a esse objeto, ela não será coletada de lixo.

Nenhuma bala de prata lá, você deve usar o Profiler para identificar coleções que mantêm esses objetos desnecessários e encontre o local no código onde eles deveriam ter sido removidos. Como Jepere disse, as coleções estáticas são o primeiro lugar para se olhar.

Um candidato óbvio são objetos com Finalisers. Eles podem demorar enquanto seu método finalize é chamado. Eles precisam ser coletados e finalizados (geralmente com apenas um único thread finaliser) e depois coletados novamente.

Esteja ciente de que você pode obter um OOMe porque o GC não conseguiu coletar memória suficiente, apesar de realmente haver o suficiente para que a solicitação de objeto seja criada. Caso contrário, o desempenho traria no chão.

Fique de olho nos recipientes estáticos. Quaisquer objetos em um recipiente estático permanecerão enquanto a classe for carregada.

Editar: Removido observação incorreta sobre a referência fraca.

Acabei de ler um artigo sobre isso, mas lamento não me lembrar de onde. Eu acho que poderia estar no livro "Java eficaz". Se eu encontrar a referência, atualizarei minha resposta.

As duas lições importantes descritas são:

1) Os métodos finais informam ao GC o que fazer quando ele derruba o objeto, mas não pede que isso o faça, nem existe uma maneira de exigir que ele faça.

2) O equivalente moderno do "vazamento de memória" em ambientes de memória não gerenciados, são as referências esquecidas. Se você não definir todas as referências a um objeto para nulo Quando você terminar, o objeto vai Nunca ser abatido. Isso é mais importante ao implementar seu próprio tipo de coleção ou seu próprio invólucro que gerencia uma coleção. Se você tem uma piscina ou uma pilha ou uma fila, e não define o balde para nulo Quando você "remove" um objeto da coleção, o balde em que o objeto estava mantém esse objeto vivo até que esse balde seja definido para se referir a outro objeto.

Isenção de responsabilidade: Conheço outras respostas mencionaram isso, mas estou tentando oferecer mais detalhes.

Eu usei o Profiler do YourKit Java (http://www.yourkit.com) para otimizações de desempenho no Java 1.5. Ele tem uma seção sobre como trabalhar em vazamentos de memória. Eu acho útil.

http://www.yourkit.com/docs/75/help/performance_problems/memory_leaks/index.jsp

Você pode obter uma avaliação de 15 dias: http://www.yourkit.com/download/yjp-7.5.7.exe

Br,
~ A

As coleções já foram mencionadas. Outro local difícil de encontrar é se você usar vários carregadores de classe, pois o antigo carregador de classe pode não poder ser coletado de lixo até que todas as referências tenham desaparecido.

Verifique também a estática - estes são desagradáveis. As estruturas de registro podem manter as coisas abertas, o que pode manter as referências em apêndices personalizados.

Você resolveu o problema?

Algumas sugestões:

  • Mapas ilimitados usados ​​como caches, especialmente quando estáticos
  • ThreadLocals em aplicativos de servidor, porque os threads geralmente não morrem, então o ThreadLocal não é liberado
  • Internando strings (Strings.intern()), o que resulta em uma pilha de Strings no PermSpace

Se você está recebendo erros de OOM em um idioma coletado de lixo, geralmente significa que há alguma memória não sendo contabilizada pelo coletor. Talvez seus objetos mantenham recursos que não sejam de Java? Nesse caso, eles devem ter algum tipo de método "próximo" para garantir que o recurso seja lançado mesmo que o objeto Java não seja coletado em breve.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top