[RESOLVIDO] Liberar memória ou cache da sessão do Hibernate

10 respostas
D

Olá,
Estou desenvolvendo um modulo de carga de imagens em um sistema, porem estou deparando com um problema.
Quando executo um operação de carga de muitos arquivos (15000) o processamento vai para 99% e o consumo de memória chega aos 2gb e poucos.

Pelo meu leigo conhecimento, até mesmo pq estar iniciando nisso agora, acredito que o hibernate está acumulando os objetos em memória, quero força para ele retirar da memória.

1 - seleciono uma pasta,
2 - eu envio os arquivos para o banco de dados
3 - não preciso que fique em memório pq so irei enviar e não usarei para o objeto.

Caso alguem passou por isso, fico no aguardo.

10 Respostas

Hebert_Coelho

Vc está salvando as imagens dentro do banco de dados? É isso?

No seu código tem tipo uma lista e você itera sobre a coleção?

D

Exato, estou gravando no banco de dados as imagens.
leio do diretorio e envio para o banco de dados.

o objeto é simples e a classe recebe uma imagem por vez.

via servelet.

estou usando spring + hibernate(jpa) + flex

Hebert_Coelho

Teria como você o código na hora que salva? E quem chama o método?

Estou imaginando que você deve um for, e se realmente tiver, você poderia colocar um flush durante o processo.

D
Servlet de upload.
protected synchronized void processRequest(HttpServletRequest request, HttpServletResponse response) throws Exception
	{
		...
		try
		{
			response.setContentType("text/html;charset=UTF-8");
			DiskFileItemFactory factory = new DiskFileItemFactory();
			factory.setSizeThreshold(4096);

			ServletFileUpload upload = new ServletFileUpload(factory);
			upload.setFileSizeMax(maxPostSize);

			@SuppressWarnings("unchecked")
			List<FileItem> fileItems = upload.parseRequest(request);
			Iterator<FileItem> iter = fileItems.iterator();
			while (iter.hasNext())
			{
				FileItem item = iter.next();
				if (!item.isFormField())
				{
					...
					out = response.getWriter();

					File file = new File(System.getProperty("java.io.tmpdir") + "/" + item.getName());
					item.write(file);
					byte[] i = FileUtils.readFileToByteArray(file);

					DocService docService = (DocService) getServletContext().getAttribute("docServiceImpl");
					docService.sendDoc(grandParentName, parrent, tipoDocID, docName, pag, nmImagem, extImagem, i);
				}
			}
		}
		catch (TransformerConfigurationException | FileUploadException | IOException | SAXException | NullPointerException e)
		{
			e.printStackTrace();
		}
		catch (Exception e)
		{
			throw e;
		}
		finally
		{
			out.close();
		}
	}
Service
@Autowired
	private ImagenService imagenService;

	@Override
	@Transactional
	@RemotingExclude
	public BasicXML sendDoc(String grandParentName, String parrent, Integer tipoDocID, String docName, Integer pag, String nmImagem, String extImagem, byte[] imagem)
			throws UnsupportedEncodingException
	{
...
				i = new Imagen();
				i.setDoc(d);
				i.setDataCriacao(getSQLTime());
				i.setPagina(pag);
				i.setNmImagem(nmImagem);
				i.setParteCd(1);
				i.setExtensaoImagem(extImagem);
				i.setImagem(imagem);
				imagenService.save(i);
...
		return bXML;
	}
ImagenService
@Service
@RemotingDestination
public class ImagenServiceImpl implements ImagenService
{
	@Autowired
	private ImagenDAO imagenDAO;

	@Override
	@Transactional
	@RemotingInclude
	public Imagen findById(Integer id)
	{
		return imagenDAO.find(id);
	}

	@Override
	@Transactional
	@RemotingExclude
	public Imagen searchUnique(ISearch search)
	{
		return imagenDAO.searchUnique(search);
	}

	@Override
	@Transactional
	@RemotingExclude
	public boolean save(Imagen imagen)
	{
		return imagenDAO.save(imagen);
	}
}
Hebert_Coelho

Cara, não sei como funciona direito no Spring, mas eu faria algo assim.

FileDAO teria um método salvar que aceitaria uma Lista de arquivos ainda não salvos.

E lá dentro, a cada 20 arquivos salvos faria um entityManager.commit() e depois entityManager.flush().

D

Resolveria parcial porem não seria uma abordagem ideal para o meu problema.

Como não posso prever quantos arquivos serão enviados não te como defenir isso.
O usuário pode enviar 1 como pode enviar 20mil.
Estou trabalhando com serviço de envio que por servlet é enviado um arquivo e efetuado a opção de salvar o mesmo,

Uma liberação de cache (acho que o hibernate deva estar fazendo isso) poderia resolver o meu problema.

a função flush faz o que exatamente?

Hebert_Coelho

dsrodrigues:
Resolveria parcial porem não seria uma abordagem ideal para o meu problema.

Como não posso prever quantos arquivos serão enviados não te como defenir isso.
O usuário pode enviar 1 como pode enviar 20mil.
Estou trabalhando com serviço de envio que por servlet é enviado um arquivo e efetuado a opção de salvar o mesmo,

Uma liberação de cache (acho que o hibernate deva estar fazendo isso) poderia resolver o meu problema.

a função flush faz o que exatamente?

Qual a diferença entre iterar dentro do DAO ou no Servlet? Eu sugeri o DAO pois lá dentro ficaria mais fácil de você controlar a sessão/transação.

O flush força a liberação de todas as alterações que estão sendo guardadas para liberar memoria. [=

As vezes quando você faz o commit ele não escreve na hora no banco de dados. Ele espera mais um cadin.

Com o flush é como se vc falasse: “Vai lá filhão!”

D

Eu acho quem gerencia a sessão/transação é o spring/hibernate o que eu faço é pegar a sessionfactory que fou injetado pelo spring.
Posso não ter certeza, tbm estou usando DAO generico “hibernate-generic-dao” um framework code.google.com/p/hibernate-generic-dao/
no meu service tenho acesso ao SF quem sabe tenha algum metodo que faça essa liberação. Até pode ser erro de configuração. estou pesquisando.

L

Nâo sei se foi intencional, mas vc está sincronizando o processamento, quer dizer que só tratará uma requisição por vez. Mas esse não é o problema.

Vc deve tentar otimizar a leitura e gravação dos arquivos, tipo o FileItem tem o método get() que retorna os bytes, acho que não precisa gravar em disco novamente para depois ler o arquivo. Se não utilizar o retorno do método sendDoc crie um método que retorne void e não crie objetos que não serão utilizados(por isso o void). Além disso deve alterar as configurações da VM para GC.

Outro coisa que vc pode fazer e processar assíncrono, afinal deve demorar bastante processar 20 mil imagens. Tipo em vez de do loop no Servlet o loop no serviço, ficaria melhor. O Spring possui anotações que faz isso de forma transparente.

D

eu preciso que seja sincronizada até mesmo por causa de umas regras de negócio (ainda não pensei em lock quem sabe resolva, mas é para outro momento).

Estou usando alguns parametros de GC, contudo estou realizando testes dentro do eclipse. Vou fazer uns testes direto no servidor e ver o comportamento.

Tentarei ver uma solução via código mesmo.

EDIT:
Procurando encontrei o metodo evict que porece ser o que preciso, vou ler mais sobre e realizar os testes.

EDIT2:
Resolvi provisóriamente. Acredito que não seja a melhor forma ou a ideal, por enquanto para testes e até mesmo para apresentação está ok.
O consumo de memoria não variou ficou em torno de 450mb e o processamento em 4% a 19%.

Utilizei a cada envio de imagem no finally esses dois comandos.

sf.getCurrentSession().flush();
sf.getCache().evictEntityRegions();

Em que o sf é o sessionfactory.
A dica que jakefrog deu: "Vai lá filhão!"
E o evict que ser para remover qualquer objeto da sessão.

Criado 23 de novembro de 2011
Ultima resposta 24 de nov. de 2011
Respostas 10
Participantes 3