tenho que fazer uma query que retorna em torno de 1 milhão de registros. Antes que alguém questione, sim ou precisar de todos os registros e sei que é uma carga muito grande, porém necessária. Já estou usando query nativa e tentei os seguintes cenário:
1 - query normal = (Java Heap Space)
2 - query com paginação, recuperando de N em N registros = (Java Heap Space)
3 - query com paginação, usando @ResultSetMapping = (Java Heap Space)
4 - query recuperando apenas as chaves primarias, e depois um loop para recuperar os dados restantes registro por registro = (Java Heap Space) (Esse cenário recuperou mais registros antes do estouro)
O Servidor tem 512MB de Heap, alguem tem alguma sugestão para esse caso?
1 - Aumente sua memória (não sei precisar o quanto seria necessário para esses milhões);
2 - Revise o passo 2 pra descobrir como que o resultado retornou. Se você paginou, mas trouxe tudo pra dentro da JVM de uma vez só, ou o primeiro resultado da paginação for muito alto, ainda assim pode vazar sua memória;
Faça a query com paginação, mas a cada consulta chame o Garbage Collection do java e de Commit (Hibernate commit) no banco para o banco liberar memoria isso sempre resolve. Ja tive esse problema.
Só um lembrete: não é recomendado chamar o GC via código (inclusive tem um parâmetro na JVM que desabilita essas chamadas).
Outro ponto: chamar o GC não é realmente chamar o GC. Isso é somente uma “dica” pra que ele, se possível, execute. Não há garantias de que ele vai executar e justamente por isso não é recomendado recair sobre o GC para resolver esse tipo de problema.
O melhor, nesses casos, é liberar as referências ao objeto utilizado. Se você, por acaso, estiver adicionando eles em uma coleção, isso não será possível. Não há problema algum você fazer o processamento no loop, desde que libere as referências aos objetos no final de cada iteração.
Agora, com 512MB de Heap, não dá pra fazer milagres pois você ainda tem que computar espaço para o stack das threads, o footprint do servidor de aplicações (se é que você está usando um), os outros objetos envolvidos na aplicação. No final você não terá os 512MB disponíveis para essa operação.
Se você adicionar um milhão de registros na lista, não adianta nada você fazer a paginação. A memória vai estourar do mesmo jeito. (Se foi bem isso que eu entendi com sua colocação.)
Você realmente precisa de todos os registros durante todo o processamento? Se sim, use um profiler para mensurar a quantidade de memória consumida neles para ter noção de quanto vai precisar porque não vai dar pra fazer milagres com 512MB de Heap. (Claro que depende do tamanho dos objetos que você aloca.)
Se você realmente precisar de todos esses objetos durante todo o processamento, considere distribuí-lo em outros processos (caso possível, já que você disse que não poderia aumentar a memória - lembrando que isso é um chute porque você não disse nada sobre o seu ambiente, por isso estou considerando a possibilidade de um cluster aí).
Só um lembrete: não é recomendado chamar o GC via código (inclusive tem um parâmetro na JVM que desabilita essas chamadas).
Outro ponto: chamar o GC não é realmente chamar o GC. Isso é somente uma “dica” pra que ele, se possível, execute. Não há garantias de que ele vai executar e justamente por isso não é recomendado recair sobre o GC para resolver esse tipo de problema.
O melhor, nesses casos, é liberar as referências ao objeto utilizado. Se você, por acaso, estiver adicionando eles em uma coleção, isso não será possível. Não há problema algum você fazer o processamento no loop, desde que libere as referências aos objetos no final de cada iteração.
Agora, com 512MB de Heap, não dá pra fazer milagres pois você ainda tem que computar espaço para o stack das threads, o footprint do servidor de aplicações (se é que você está usando um), os outros objetos envolvidos na aplicação. No final você não terá os 512MB disponíveis para essa operação.[/quote]
[quote=ninjasauro]Como eu libero a memoria na paginação?
A paginação é feita em loop. Creio que a memoria só vai liberar quando sair do loop.
Existe uma maneira melhor de paginar? Que não seja em loop?[/quote]
Faça algo como:
long totalRegistroNoBanco = // um countNaTablea
long totalDePagainas = totalRegistroNoBanco / 50; // chutei 50 aqui
for (0 ---> totalDePaginas){
List<Registro) registroList = consultaPaginada(dadosPaginacao); // seria tipo página 1, 50 registros // depois 2, 50 registros
// trabalhe com a lista
}
um Ponto importante que não sei se tem haver, é que o construtor do objeto do resultado tem mais de 30 parâmetros e fiz uns testes com apenas um parâmetro e funcionou. Essa quantidade de parâmetros no construtor não libera na memoria?
um Ponto importante que não sei se tem haver, é que o construtor do objeto do resultado tem mais de 30 parâmetros e fiz uns testes com apenas um parâmetro e funcionou. Essa quantidade de parâmetros no construtor não libera na memoria?[/quote]
Você viu oq o Ataxexe escreveu sobre GC?
não, seu código não está igual ao meu. Em meu código a lista é liberada da memória, no seu você só adiciona. Se você ficar só adicionando, nunca vai funcionar com pouca memória. Você terá que aumentar a memória do servidor.
Outra solução seria, com o tipo de paginação que eu passei você sintetizaria os dados em uma tabela temporária. Não tem como ajudar muito sem saber qual o requisito.
OBS.: Nenhum tipo de paginação do mundo vai te ajudar se você for pegar todos os resultados e jogar em memória. Dado em memória requer mais memória… sempre.
realmente existe essa diferença mesmo. Eu libero todas as variáveis menos a lista com os dados pois eu preciso dela.
se efetivamente limpar ela ai funciona como você disse. A sua sugestão da tabela temporária seria gravar em arquivo no disco?
realmente existe essa diferença mesmo. Eu libero todas as variáveis menos a lista com os dados pois eu preciso dela.
se efetivamente limpar ela ai funciona como você disse. A sua sugestão da tabela temporária seria gravar em arquivo no disco?[/quote]
Eu salvaria em uma tabela no banco que poderia ter os dados apagados após o processamento.
Seria algo como:
// busca os dados paginando de 50 em 50 (por exemplo)
// faz um sumário desses dados e persiste na tabela temporária
// ao terminar o loop em todos os registros
// trabalhar em cima dos dados dessa tabela temporária
// drop na tabela temporária
Por que você precisa ter esse 1 milhão de objetos na memória ao mesmo tempo?
Que tipo de processamento fará com essa lista?
Chutando duas situações:
Está agregando esses valores de alguma forma: Não dá pra agregar direto no banco?
Está processando item por item da lista sem um precisar de outro: Experimente retornar um iterator ao invés de uma lista fazendo lazy loading dos dados do banco (paginando resultados).
consegui resolver o problema da seguinte maneira.
Fiz a correção que o Hebert citou para limpar da memoria da lista completo de registros.
Agora eu não guardo mais os registros em uma lista.
Agora eu faço a query paginada e serializo cada registro em um arquivo. Cada pagina da paginação ficou referente ai um arquivo serializado. Assim posso liberar a memoria da lista que estava estourando a memoria.
Fiz a serialização dos objetos com o java.io.ObjectOutputStream e está funcionando sem problemas.
Preciso desses registros para geração de um relatório. O único trabalho depois é desserializar os arquivos mas o ObjectOutputStream trabalha muito bem com isso.