Afim de possibilitar a geração de relatórios relativamente grandes, o JasperReports dispõe de um recurso chamado “virtualização”. Ao gerar um relatório utilizando virtualização, o JasperReports busca gerenciar melhor a memória RAM utilizada para geração do mesmo, tentando assim, eliminar um grande incômodo chamado “OutOfMemoryException: Java heap space”.

Existem 3 tipos de virtualização: JRFileVirtualizer, JRSwapFileVirtualizer, e JRGzipVirtualizer. Cada uma gerencia a memória de forma distinta, cabe ao desenvolvedor identificar a situação e a que melhor se adequar ao seu problema. Neste artigo, pretendo explicar o funcionamento de cada um deles.

Observação Importante:
Podemos dividir o processo de geração de um relatório em 2 fases:
1º: Preenchimento (ou Fill Phase): Nesta fase, busca-se os dados da fonte de dados (banco de dados, java beans, etc..), percorre esses dados gerando então o relatório final, podendo ser um objeto JasperPrint ou serializado em um arquivo “*.jrprint”. Ambos os formatos, são o relatório já pronto, crú, sem formato, podendo ser exibido apenas através da GUI fornecida pelo JasperReports. Nesta fase, o JasperReports não precisa se utilizar de APIs de terceiros para a criação do relatório em sí.
2º: Exportação (ou Export Phase): Nesta fase, é realizado apenas a conversão do relatório já pronto (JasperPrint) para algum formato conhecido, usando APIs de terceiros, tais como: PDF usando a iText, XLS usando JExcelApi ou Apache POI, e muitos outros.

É extremamente importante saber que o recurso de virtualização aplica-se exclusivamente à fase de Preenchimento (Fill Phase). O JasperReports não tem controle de como as APIs de terceiros geram o formato específico (pdf, xls, docx, etc).

Portanto é necessário tomar cuidado com o formato de destino na fase de Exportação, pois dependendo do tamanho do relatório, ainda pode-se ter problemas. Ao exportar para xls, por exemplo, utilizando a JExcelApi, ela precisa que todo o conteúdo da planilha esteja em memória durante a criação do xls, logo é bem perigoso ter um OutOfMemory aqui, mesmo tendo gerado o JasperPrint com sucesso.  Em casos como este, não há muita escolha, senão aumentar o Heap Space da JVM e a  memória RAM da máquina.

JRFileVirtualizer – [net.sf.jasperreports.engine.fill.JRFileVirtualizer]
Construtores:
JRFileVirtualizer(int maxSize)
JRFileVirtualizer(int maxSize, String directory)

Para este virtualizador, devemos informar o número de páginas que serão mantidas em memória (objetos JRPrintPage), e opcionalmente, um diretório onde as páginas em excesso serão salvas em arquivos, liberando a RAM, caso não seja informado um diretório, será utilizado o diretório em que a aplicação está rodando. A maior desvantagem desse virtualizador é um overhead na manipulação de arquivos. Ele cria muitos arquivos em disco em um único diretório durante o processo de virtualização, para então juntar todos eles e gerar o arquivo final *.jrprint (ou objeto JasperPrint). Se o dataset não for tão grande assim, pode-se utilizar esse virtualizador tranquilamente.

Se o numero de páginas informadas ao virtualizador for menor que o número de páginas que o relatório terá, o virtualizador não será utilizado, obviamente. Por exemplo: Se informado para manter no máximo 100 páginas em memória, e a fase de preenchimento gerou o relatório com apenas 80 páginas, o virtualizador nem chegou a ser usado.

conexao = Conexao.getConexao()
 
//Instancia o virtualizador, indicando a pasta onde serão gerados os arquivos
JRAbstractLRUVirtualizer virtualizer = new JRFileVirtualizer(100, "c:\\tmp");
 
//Seta o parametro REPORT_VIRTUALIZER com a instância da virtualização
HashMap parametros = new HashMap();
parametros.put(JRParameter.REPORT_VIRTUALIZER, virtualizer);
 
//Preenche o relatório e exibe numa GUI
JasperPrint jp = JasperFillManager.fillReport("MeuRelatorio.jasper", parametros, conexao);
JasperViewer.viewReport(jp);

JRSwapFileVirtualizer – [net.sf.jasperreports.engine.fill.JRSwapFileVirtualizer]
Construtores:
JRSwapFileVirtualizer(int maxSize, JRSwapFile swap)
JRSwapFileVirtualizer(int maxSize, JRSwapFile swap, boolean swapOwner)

Este virtualizador elimina a desvantagem do JRFileVirtualizer. Ao invés de criar vários arquivos em disco, ele cria apenas um único arquivo de swap, que pode ir crescendo de tamanho conforme a necessidade Este arquivo de Swap pode inclusive ser utilizado por vários relatórios ao mesmo tempo. Para este virtualizador, deve-se informar:

– O número máximo de páginas mantidas em memória, igual ao JRFileVirtualizer;
– Uma instância de um objeto JRSwapFile, que representa o arquivo que será usado como swap;
– O terceiro parâmetro serve para dizer se o arquivo de swap será utilizado apenas nesta instância do JRSwapVirtualizer. Em caso de true, ele executa o método cleanup() ao terminar de ser utilizado, apagando o arquivo swap do disco.

Igualmente ao JRFileVirtualizer, este virtualizador só começa a ser utilizado se o número de páginas do relatório ultrapassar o número de páginas que devem ser mantidas em memória, no parâmetro “maxSize”.

conexao = Conexao.getConexao();
 
//Instancia o arquivo de swap, informando:
// Diretorio,
// Tamanho de cada bloco (4kb)
// Numero mínimo de blocos que o  swap será aumentado sempre que estiver cheio
JRSwapFile arquivoSwap = new JRSwapFile("c:\\tmp", 4096, 100);
 
// Instancia o virtualizador
JRAbstractLRUVirtualizer virtualizer = new JRSwapFileVirtualizer(100, arquivoSwap, true);
 
//Seta o parametro REPORT_VIRTUALIZER com a instância da virtualização
HashMap parametros = new HashMap();
parametros.put(JRParameter.REPORT_VIRTUALIZER, virtualizer);
 
//Preenche o relatório e exibe numa GUI
JasperPrint jp = JasperFillManager.fillReport("MeuRelatorio.jasper", parametros, conexao);
JasperViewer.viewReport(jp);

JRGzipVirtualizer – [net.sf.jasperreports.engine.fill.JRGzipVirtualizer]
Construtor:
JRGzipVirtualizer(int maxSize)

Diferente dos outros dois virtualizadores, o JRGzipVirtualizer não escreve nada em arquivo físico. Ele otimiza o consumo de memória durante a geração, comprimindo as páginas diretamente na memória RAM, utilizando-se de um algoritmo GZIP. De acordo com a documentação oficial, ele reduz o consumo de memória num fator de 1/10.

conexao = Conexao.getConexao();
 
//Instancia o virtualizador
JRAbstractLRUVirtualizer virtualizer = new JRGzipVirtualizer(100);
 
//Seta o parametro REPORT_VIRTUALIZER com a instância da virtualização
HashMap parametros = new HashMap();
parametros.put(JRParameter.REPORT_VIRTUALIZER, virtualizer);
 
//Preenche o relatório e exibe numa GUI
JasperPrint jp = JasperFillManager.fillReport("MeuRelatorio.jasper", parametros, conexao);
JasperViewer.viewReport(jp);

Então é isso pessoal. Caso tenham alguma dúvida a respeito, podem perguntar nos comentários, ou então peça um help pelo grupo de discussão: http://groups.google.com.br/group/jasperreports-ireport-brasil

Se escrevi algo que consirem errado, ou falta algo, por favor, não deixem de comentar ok.

Abraço!

Fontes Principais:
Livro: The JasperReports Ultimate Guide
Site / Fórum: http://jasperforge.org/projects/jasperreports