«

»

Sep 07

Problemas de performance na sua aplicação Java? Ela esta utilizando memória demais? Será que não é culpa sua?

Imagine a seguinte tarefa:
Criar duas telas para um blog, a primeira lista as categorias dos posts, junto com a quantidade de posts de cada categoria, a segunda lista todos os posts de uma categoria, com paginação e por padrão ira mostrar 20 posts por página …
para isto você tem ja pronta a seguinte estrutura:

  • Para persistência esta sendo utilizado JPA
  • Para view esta sendo utilizado JSP puro
  • Para mostrar tabelas esta sendo utilizada a DisplayTag
  • Para simplificar o exemplo, a lógica fica implementada em servlets

Qual é o exemplo padrão para listar os posts de uma categoria com paginação?

Código do servlet:

        Categoria c = entityManager.find(Categoria.class,1);
        request.setAttribute("categoria",c);
        request.setAttribute("posts",c.getPosts());
        request.getRequestDispatcher("/resposta.jsp").forward(request,response);

Código da tabela com paginação:

<h2>${categoria.nome}</h2>
<display:table name="posts" pagesize="10"/>

Isto vai fazer o que foi solicitado, que é mostrar de uma forma paginada, os posts da categoria de id=1 (eu fixei o ID para simplificar o exemplo)

Quais quais os prováveis problemas de performance você já identificou?
Nenhum ainda?
Este tipo de código é bastante comum de se ver por ai, por este tipo de código digo, buscar todos os registros e paginá-los depois …
Inclusive em algumas situações (semana passada), por facilidade eu fiz coisa bem parecida (claro que este é um exemplo simplista, mas não é este o ponto) …

Qual o problema então?
Imagine uma categoria com 100.000 posts …
Cada vez que a página for carregada vai acontecer o seguinte:

  1. Sera feita uma consulta ao banco pela categoria
  2. Quando a propriedade posts for lida, sera feita uma segunda consulta trazendo todos os 100.000 registros para memória
  3. Para cada um dos 100.000 registros, sera criado um objeto (isto ainda adiciona o tempo de criar o objeto e setar as propriedades buscadas dos campos)
  4. os 100.000 registros serão passados para a display tag (ou a sua tag library de paginação de preferência)
  5. a display tag vai renderizar as 10 linhas que estão configuradas e não vai fazer nada com o resto do montão de objetos que foi carregado na memória por preguiça do programador, ou por que simplesmente o cara não sabia como fazer diferente

Ou seja, isto é um desperdício de processamento e memória, que consome tempo do usuário …

Este ciclo todo vai ocorrer novamente quando o usuário pressionar o numero da página desejada também, que vai novamente entulhar a memória de lixo que não vai ser utilizado …

Isto não é uma critica a display tag, e sim aos programadores (eu incluso, pois como comentei fiz parecido em um sistema, e passei dois dias esta semana corrigindo isto).

E como resolver?
Buscar do banco de dados apenas os registros que serão utilizados …
Claro, isto vai requerer no mínimo duas consultas, uma para trazer o conteúdo e outra para trazer a contagem total de registros …
Mas a melhora é visível na performance de acesso a esta tela …

E como implementar isto com display tag?
Tem duas opções:
utilizar os atributos da tag partialList e size (claro que fazendo isto você precisa fazer ordenação externa também se quiser algma coluna ordenavel na tabela) e utilizar o helper da display tag para descobrir qual o primeiro registro a ser mostrado:

   (Integer.parseInt(request.getParameter((new ParamEncoder(tableId).encodeParameterName(TableTagParameters.PARAMETER_PAGE)))) - 1) * pageSize

ou retornar em vez de uma collection uma implementação da interface org.displaytag.pagination.PaginatedList

Mas se você estiver utilizando por exemplo o paginator do tomahawk com JSF então boa sorte pois ele não tem um suporte muito bom para este tipo de coisa …
No caso de JSF+EJB3 acho mais indicado utilizar algo parecido com o p4j5, ou então não utilizar nenhum dos componentes de paginação prontos que eu conheço para JSF :D

Então, vocês escrevem código parecido com isto? (trazer um monte de registros e paginar só na view)
Conhecem gente que faz isto?

Que outras coisas vocês acham que fazem ou que viram fazer (as vezes colocar a culpa nos outros facilita o comentário :D ) que são visivelmente um problema grave de performance quando a aplicação sai do ambiente de testes?
Digo quando sai do ambiente de testes por que mesmo o exemplo citado em um ambiente com poucos dados não apresenta problema algum …

E de quem é a culpa deste tipo de código ser tão comum por ai?
Na minha opinião, é da falsa sensação de programação statefull que temos programando com a grande maior parte dos componentes disponíveis por ai …