Blog do Urubatan
msgbartop
Desenvolvedor, Palestrante, Escritor, Nerd Assumido e Pai do Marcus :D
msgbarbottom

25 Sep 06 Não seguir as proprias regras sempre da porcaria (problemas de aplicação travando)

Vocês ja ouviram certamente aquela fraze: se morder a lingua morre envenenado …

Pois é, quase aconteceu comigo esta semana …
Quando algum programa esta com problemas eu sou o primeiro a perguntar se é a ultima versão, se não é digo para atualizar, pois acredito que só se pode reclamar dos bugs da ultima versão de alguma coisa, pois o problema pode ja ter sido corrigido …

Estava apanhando a alguns dias para uma aplicação que estava travando sem motivo aparente.
Até que resolvi criar um aspecto para fazer um trace do código, e como a principio eu não tinha nem ideia de onde a app estava travando (tah, eu até tinha uma noção de depois do que era, mas o que exatamente eu não sabia, e podia ser qualquer uma das classes usadas daquele ponto para baixo) o aspecto ficou mais ou menos assim:


public aspect MemoryLeakDetector {
  private static Logger logger = Logger.getLogger(MemTrace.class);

  private static final String SPACE = "__";

  ThreadLocal cd = new ThreadLocal() {

    protected Object initialValue() {
      return new MemTrace();
    }

  };

  /**
   * The constructors in those classes - but only the ones with 3 arguments.
   */
  pointcut constructors(): execution(new(..)) && !within(MemoryLeakDetector) && !within(MemTrace) && !within(ThreadLocal);

  /**
   * This specifies all the message executions.
   */
  pointcut methods(): execution(* *(..)) && !within(MemoryLeakDetector) && !within(MemTrace) && !within(ThreadLocal);

  before(): methods(){
    traceBefore(thisJoinPoint, false);
  }

  after(): methods(){
    traceAfter(thisJoinPoint, false);
  }

  before(): constructors(){
    traceBefore(thisJoinPoint, true);
  }

  after(): constructors(){
    traceAfter(thisJoinPoint, true);
  }

  private void traceAfter(JoinPoint jp, boolean isConstructor) {
    MemTrace cl = (MemTrace) cd.get();
    if (logger.isInfoEnabled()) {
      String cls = jp.getSignature().getDeclaringTypeName();
      String met = jp.getSignature().getName();
      String threadName = Thread.currentThread().getName();
      long free = Runtime.getRuntime().freeMemory();
      long max = Runtime.getRuntime().maxMemory();
      long total = Runtime.getRuntime().totalMemory();
      long milis = System.currentTimeMillis();
      String msg = StringUtils.join(new Object[] {
          StringUtils.repeat(SPACE, cl.get()), "<",
          Integer.valueOf(cl.get()), threadName, cls, met,
          Long.valueOf(free), Long.valueOf(max), Long.valueOf(total),
          Long.valueOf(milis) }, " | ");
      logger.info(msg);
      if (logger.isDebugEnabled()) {
        msg = StringUtils.join(jp.getArgs(), ", ");
        logger.info(StringUtils.repeat(SPACE, cl.get()) + msg);
      }
    }
    cl.dec();
  }

  private void traceBefore(JoinPoint jp, boolean isConstructor) {
    MemTrace cl = (MemTrace) cd.get();
    cl.inc();
    if (logger.isInfoEnabled()) {
      String cls = jp.getSignature().getDeclaringTypeName();
      String met = jp.getSignature().getName();
      String threadName = Thread.currentThread().getName();
      long free = Runtime.getRuntime().freeMemory();
      long max = Runtime.getRuntime().maxMemory();
      long total = Runtime.getRuntime().totalMemory();
      long milis = System.currentTimeMillis();
      String msg = StringUtils.join(new Object[] {
          StringUtils.repeat(SPACE, cl.get()), ">",
          Integer.valueOf(cl.get()), threadName, cls, met,
          Long.valueOf(free), Long.valueOf(max), Long.valueOf(total),
          Long.valueOf(milis) }, " | ");
      logger.info(msg);
      if (logger.isDebugEnabled()) {
        if (jp.getArgs() != null && jp.getArgs().length > 0)
          msg = StringUtils.join(jp.getArgs(), ", ");
        else
          msg = " ";
        logger.info(StringUtils.repeat(SPACE, cl.get()) + msg);
      }
    }
  }

}

public class MemTrace {
  private int depth = 0;

  public void inc() {
    depth += 1;
  }

  public void dec() {
    depth -= 1;
  }

  public int get() {
    return depth;
  }
}

Bom, descobri que o problema era o envio de e-mails, que eu estava fazendo utilizando o Commons-Email.
primeiro passo foi tentar passar alguns parametros para o JavaMail no inicio da aplicação …
o script de inicialização fiou parecido com isto:
java -cp $CP -Dmail.smtp.connectiontimeout=20000 -Dmail.smtp.timeout=20000 ….

Pois é, não adiantou absolutamente nada …
A aplicação continuou travando no meio do envio de uma mensagem (coisa que é feita dentro de um loop que é chamado de dentro de uma thread, que ….)
E não era nem no primeiro e-mail, isto simplesmente acontecia em algum momento no tempo …

Segunda tentativa, removi o Commons-Email e substitui pelo suporte a JavaMail do Spring Framework, o código até ficou bem simples …
Mas continuou tudo na mesma :S

Ja estava quase entrando em desespero quando resolvi finalmente ir ao site do JavaMail …
A versão do jar que estava no sistema é a 1.3.1 que vem junto com o Spring Framework 2.0rc2, no site do JavaMail tinha a versão 1.4 para baixar.

Pois resumindo toda a história, substitui o mail.jar pelo que veio junto com o JavaMail 1.4 e pronto, tudo funcionando novamente :(

E eu que ja estava até pensando em criar um sistema de pooling de threads com timeout de execução, tipo se a tarefa demorasse mais do que o timeout especificado a thread seria derrubada :D
não foi necessário, foi só fazer o que eu sempre falo, utilizar a versão mais atual da biblioteca …

Bom, este post foi apenas para lembrar quem se der o trabalho de ler …

Se nada funcionar: leia o manual
se continuar não funcionando: faça um upgrade que deve resolver
se ainda não deu certo: ai é hora de arregaçar as mangas, chutar o balde e arrumar de verdade o que ainda não funciona, se esta não for uma opção …
As vezes uma gambi chinelona destas qe eu comentei antes podem salvar a sua vida, mas lembre de documentar bem quando fizer isto …

PS.: AspectJ rocks!! a unica coisa chata é ter que compilar novamente a app, por que o loadtime weaving não ta as mil maravilhas ainda :D

If you enjoyed this post, make sure you subscribe to my RSS feed!

Tags: , , ,

Leave a Comment