Blog do Urubatan
msgbartop
Desenvolvedor, Arquiteto, Palestrante, Coordenador do RSJUG, Patinador e Blogger
msgbarbottom

07 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 …

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

Tags: ,

Reader's Comments

  1. |

    Passei por esse problema há algumas semanas. Aqui utilizo a lógica de negócios numa camada de EJBs e a view com JSF (Woodstock) em outra. Uso o table do woodstock que se alimenta de um DataProvider que se alimenta de um List.

    O fato é que precisava fazer uma consulta que retornaria um número absurdo de registros. Minha primeira opção foi especializar o DataProvider de forma a ele ter a inteligência para paginar os dados, mas aí não funcionava adequadamente o table. Se vc ainda nah viu esse componente em ação, ele possui controles p sorter e pagination nele próprio. As informações para compor isso vem do proprio List (tamanho total e o consequente número de páginas).

    Então achei nesse post http://www.ninthavenue.com.au/blog/lazy_list uma solução interessante! Colocar o mecanismo de paginação na própria List! Ou seja, o size dela retornaria o total da consulta, mas ela manteria somente os x registros lidos (onde x é o tamanho da página de dados, não necessariamente o tamanho da página mostrada). Quando o componente pedir um registro q ela não tem, submete nova consulta!

    Implementei aqui com algumas ressalvas, uma vez q por ter separado em duas camadas distantes, eu não posso manter sessão aberta ou usar uma query como ele usou. Na verdade, eu passo p lista o serviço e os métodos q ela deve utilizar p pegar o count e os registros paginados.

    No fim das contas, passo essa list p o dataprovider e o provider p o table woodstock. Para eles é tudo normal e agem como se fora um List full. Works like a charm!

    Mas como nada na vida é simples, agora surgiu a necessidade de TOTALIZAR algumas colunas da table! Ou seja, preciso de uma terceira chamada ao banco (ou alterar o método do count) para saber os totais das colunas dadas. Acho que encapsulando tudo isso daria um belo componente JSF! =)

    Btw, nice post!

    Reply to this comment
  2. |

    Urubatam, agradeço pelo Post o qual apresenta um problema em potencial, ainda mais para um iniciante como eu, você não poderia apresentar um exemplo simples de como o problema pode ser resolvido? Estou usando JSF aqui, mas como vc disse em ambiente de teste isso é impercebível.

    Já procurei essa solução em vários forums e não encontrei, poderia ajudar-nos?

    Forte abraço,

    Reply to this comment
  3. |

    A solução que o Rodrigo postou no comentário anterior funciona bem com JSF :D

    Reply to this comment
  4. |

    Sera que vc não poderia montar um exemplo simples e mais trivial para a gente poder aprender?

    Reply to this comment
  5. |

    Isso aqui pode ajudar para paginar sobre demanda,
    http://wiki.apache.org/myfaces/WorkingWithLargeTables

    Abraços.

    Reply to this comment
  6. |

    Realmente eu já fiz isso no começo, quando ainda tava aprendendo sql. + depois descobri que havia 2 parametros que são passados na consulta select (LIMIT e OFFSET) que restringem a quantidade de registros retornadas pela consulta. Acho que isso hoje em dia já não é + problema pq se tornou padrão. Existem vários módulos para isso disponíveis em várias linguagens pela net afora! :) em todo caso muito obrigado pela dica

    Reply to this comment
  7. |

    [...] possui uma implementação simples e não se preocupa com alguns requisitos não funcionais como a paginação sob demanda ou quais os objetos que deveriam permanecer entre as requisições. Além do mais o exemplo é um [...]

    Reply to this comment
  8. |

    Olá Urubatan, parabéns pelos trabalhos que vc tem desenvolvido, por várias vezes resolvi problemas com as suas explicações. Agora tenho uma dúvida, estou utilizando o netbeans 6.1 e estou tentando acessar mostrar meus dados na table do woodstock porém não conheço bem o componente. Será que vc poderia me dar uma luz. O sistema antigo que eu tenho eu utilizei o tomawalk, ja tenho uma noção muito boa!!!! obrigado!!!!!

    Reply to this comment

Leave a Comment