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

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.

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?

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

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.

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

[code]@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);
}

}[/code]

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().

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?

[quote=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?[/quote]

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!”

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.

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.

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.