Fórum Thread #3526

11/03/2009

0

Tenho um modulo de romaneios de balança rodoviária, que basicamente ele faz a pesagem dos caminhões e emissão dos romaneios (documentos de registro das pesagem) Esse é fonte da classe da Thead que fica rodando, chamando a leitura da serial que se encontra a balança e atualizando o peso e mostrando mensagens de retorno na tela do romaneio. package br.com.erp; import br.com.erp.acesso.LerConfig; import br.com.erp.tools.Converte; import br.com.erp.tools.SerialComLeitura; import java.util.Date; public class LeituraRandom extends Thread { String[] configPorta = {"COM1", "4800", "0", "7", "2", "5"}; SerialComLeitura leitura; Boolean parar = false; Date hora; LeituraRandom(String s) { super(s); configPorta = new LerConfig().lerConfigPorta(); configuraPorta(); } public void setParar(boolean p) { parar = p; } // @Override public void run() { int peso = 0; int pesoAnt = 0; int estavel = 0; Integer pes = 0; while (!isInterrupted()) { if (parar) { break; } peso = capturaPeso(); hora = new Date(); pes = peso; Romaneio.tfPesoAtual.setText(pes.toString()); if (peso != pesoAnt) { estavel = 0; } else { estavel++; } if (peso != 0) { if (estavel < 3) { Romaneio.tfTime.setText("o" + hora.getHours() + ":" + hora.getMinutes() + ":" + hora.getSeconds()); Romaneio.btCapturaPeso.setEnabled(false); } else { Romaneio.tfTime.setText("-" + hora.getHours() + ":" + hora.getMinutes() + ":" + hora.getSeconds()); Romaneio.btCapturaPeso.setEnabled(true); } } else { Romaneio.tfTime.setText("|" + hora.getHours() + ":" + hora.getMinutes() + ":" + hora.getSeconds()); Romaneio.btCapturaPeso.setEnabled(false); } hora = null; pesoAnt = peso; try { Thread.sleep(1500); } catch (InterruptedException ex) { System.out.println("Erro na Thread: " + ex); leitura.setPeso("Erro na Thread: " + ex); } if (leitura.getErro()) { Romaneio.tfAvisoBal.setText("Leitura com Problema...\n" + leitura.getPeso()); break; } else { Romaneio.tfAvisoBal.setText(leitura.getLinha()); } } } // } public static void main(String[] args) { LeituraRandom ts1 = new LeituraRandom("Gera"); ts1.start(); } private Integer capturaPeso() { Integer peso = 0; try { if (!leitura.getErro()) { leitura.AbrirPorta(); } if (!leitura.getErro()) { leitura.LerDados(); } //Controle de tempo da peso aberta na serial try { Thread.sleep(500); } catch (InterruptedException ex) { System.out.println("Erro na Thread: " + ex); } leitura.FecharCom(); if (leitura.getErro()) { peso = 0; } else { peso = Converte.converteInt(leitura.getPeso()); } } catch (Exception ex) { ex.printStackTrace(); } return peso; } private void configuraPorta() { try { int vel = Integer.parseInt(configPorta[1]); int pari = Integer.parseInt(configPorta[2]); int data = Integer.parseInt(configPorta[3]); int stop = Integer.parseInt(configPorta[4]); int toBytes = Integer.parseInt(configPorta[5]); leitura = new SerialComLeitura(configPorta[0], vel, 0, pari, data, stop, toBytes); leitura.setPeso("0"); leitura.HabilitarLeitura(); if (!leitura.getErro()) { leitura.ObterIdDaPorta(); } } catch (Exception ex) { ex.printStackTrace(); } } public void fechaPorta() { try { leitura.FecharCom(); } catch (Exception ex) { ex.printStackTrace(); } } } O PROBLEMA É O DO SEGUINTE: O PROGRAMA TRANCA DEPOIS DE 3 HORAS DE USO, AI NOTA QUE O USO DE MEMÓRIA ESTA ALTO, MAS NÃO ME RETORNO NENHUMA EXCESSÃO DIZENDO ESTOURO DE MEMÓRIA. O QUE PODE SER ISSO? O QUE POSSO FAZER NO CODIGO?
Pirahy Alimentos

Pirahy Alimentos

Responder

Posts

11/03/2009

Henrique Weissmann

Olá,

será que você poderia me reenviar o código formatado? É que a leitura do mesmo da maneira como foi postado está bastante complicada.

Responder

Gostei + 0

12/03/2009

Pirahy Alimentos

Eu teria que te mandar em arquivo, em anexo, me diz qual é outro meio porque esse meio aqui vai somente dessa maneira... aguardo seu retorno para te mandar o código...
Responder

Gostei + 0

12/03/2009

Devmedia

Olá,
vc pode mandar os arquivos para o consultor através da opção "meu disco virtual"  que fica na home da consultoria. Vc pode upar arquivos do tipo .rar e .zip. Caso tenha dúvidas, assista ao vídeo que se encontra ao lado da opção.
Responder

Gostei + 0

12/03/2009

Pirahy Alimentos

Coloquei o arquivo... são dois codigo a thread e o que faz a leitura da serial.
Responder

Gostei + 0

12/03/2009

Devmedia

Pirahy,
vc precisa mencionar ao consultor o caminho no seu disco virtual para que ele possa baixar os arquivos. Nenhum consultor tem acesso ao seu disco virtual.
Responder

Gostei + 0

12/03/2009

Pirahy Alimentos

fui pelo video do disco virtual, aqui esta o link para acessar o arquivo https://www.devmedia.com.br/imagens/discovirtual/200408/CodTeste/codigo_thread.rar
Responder

Gostei + 0

12/03/2009

Henrique Weissmann

Olá Pirahy, seguem abaixo algumas das sugestões que poderão melhorar seu código:

Primeiro na classe SerialComLeitura
Bom: dado que se trata de uma aplicação que contém um loop que, pelo que pude entender, irá ser executado o dia inteiro, devemos levar em consideração alguns pontos relativos ao uso da memória:
* Variáveis que terão valor fixo durante todo o ciclo de execução da aplicação podem ser intanciadas uma única vez
* Verificar instanciações desnecessárias.

Observe o seu método String getPeso(), por exemplo: na classe SerialComLeitura:

Você intancia um FileReader do seguinte modo:
FileReader fr = new FileReader(new File("C:\\peso.txt").getAbsoluteFile().getPath());
          
Neste caso, dado que sempre será lido o arquivo C:\peso.txt, seria uma boa alternativa definir uma variável estática na sua classe tal como

static String arquivoEntrada = new File("C:/peso.txt").getAbsolutePath();

E em seguida, o mesmo código descrito acima, poderia passar a ser algo como
FileReader fr = new FileReader(arquivoEntrada);

Isto porque assim não precisaria ficar instanciando repetidas vezes a mesma string referente ao nome do arquivo a ser lido (e acredite: strings são pesadas).

Neste caso do arquivo, poderia ser até melhorado, implementando na sua classe um código que carregue o nome do arquivo uma única vez no momento em que é instanciada. Fazendo isto, a sua aplicação ficará bem mais flexível a mudanças no futuro.

Bom, continuando na mesma classe: vamos voltar ao método getPeso()

 public String getPeso() {
        // ============================== peso lendo arquivo texto
        try {
            FileReader fr = new FileReader(new File("C:\\peso.txt").getAbsoluteFile().getPath());
            BufferedReader br = new BufferedReader(fr);
            String linArq = null;
            while ((linArq = br.readLine()) != null) {
                return linArq;
            }
            br.close();
            fr.close();
            br = null;
            fr = null;
            return peso;
        } catch (IOException ex) {
            return peso;
        } catch (Exception ex) {
            //========================== fim
            return peso;
        }
    }

Visto que você está lendo uma única linha no arquivo, tal como pode ser visto neste trecho:
FileReader fr = new FileReader(new File("C:\\peso.txt").getAbsoluteFile().getPath());
            BufferedReader br = new BufferedReader(fr);
            String linArq = null;
            while ((linArq = br.readLine()) != null) {
                return linArq;
            }
ao executar a instrução return linArq;, tudo o que vem depois não será ignorado. No caso, você pode reescrever este método da seguinte maneira:

  public String getPeso() {
        // ============================== peso lendo arquivo texto
        FileReader fr = null;
        BufferedReader br = null;
        try {
            fr = new FileReader(new File("C:\\peso.txt").getAbsoluteFile().getPath());
            br = new BufferedReader(fr);
            String linArq = br.readLine();
            return linArq == null ? peso : linArq;
        } catch (IOException ex) {
            return peso;
        } catch (Exception ex) {
            //========================== fim
            // Aqui seria interessante incluir um log de saída, para que voce saiba o que pode estar dando errado
            return peso;
        } finally {
              try {
                    br.close();
                    fr.close(); // Dentro do finally, SEMPRE estes leitores de stream serão finalizados
                } catch (IOException ex) {
                    // Segue aqui um tratamento de erro
                }
        }
    }

O que foi feito no método: inicialmente, visto que anteriormente você só lia a primeira linha do arquivo, e em seguida a retornava caso estivesse presente, os recursos alocados pelas suas instancias de FileReader e BufferedReader não eram liberados (pois havia um return logo em seguida, antes do método ser chamado).

Sendo assim, na nova versão, os recursos são finalizados dentro da cláusula finally, que sempre será executada após a execução do bloco try, tendo sido disparada uma excessão ou não, tendo sido retornado um valor ou não. Sendo assim, talvez este fosse seu problema de memória, e aqui aparece portanto uma primeira solução para o mesmo.

Tenho também uma sugestão de melhoria nesta classe com relação à variável peso: visto se tratar de um valor numérico, por que não representá-la como tal? Você poderia representá-la como uma instancia de BigDecimal. Ao ler a string, poderia simplesmente executar um código como o abaixo:

BigDecimal peso;
try {
  peso = new BigDecimal(valorString);
} catch (NumberFormatException ex) {
    /*
       Aqui dentro, voce poderia ter um tratamento de erro mais elaborado, verificando por exemplo o porque
       de sua balança não estar lhe fornecendo um valor numérico.
   */
}

Finalmente, para a mesma classe: será que o método getPeso() não deveria ser algo mais simples como
public String getPeso() {
   return this.peso;
}

e em seguida voce renomear o mesmo método getPeso() para algo como lerPeso() e transformá-lo em um método do tipo void que, ao invés de rtornar um valor, em seu final já define o valor da variável peso chamado o método setPeso()?

Sabe porque isto? Porque quando se coloca lógica de negócio dentro de um método que esteja no padrão JavaBeans (como getPeso()), se você estará no fundo disvirtuando sua função, que seria a de simplesmente te retornar o valor de um dos atributos da sua classe. É apenas uma idéia, pois assim poderá tornar o seu código mais legível e fácil de manter no futuro também.

Sugestão de legibilidade:
evite expressões como
if (Escrita == true)
se colocar
if (Escrita)
terá o mesmo resultado, e seu código será mais limpo. Além disto, será mais rápido também (melhoria praticamente imperceptível, mas real), uma vez que a comparação se tornará desnecessária.

Outro ponto interessante: seu método run() da mesma classe:

 public void run() {
        try {
            Thread.sleep(5);
        } catch (Exception e) {
            System.out.println("Erro de Thred: " + e);
            setPeso("Erro de Thred: " + e);
            setErro(true);
        }
    }

Pelo visto aqui, sua thread vai ser inicializada, porém não irá fazer nada além de dormir cinco segundos sempre. Lembre-se: é no método run que se encontram as atividades a serem executadas pela sua thread.

Agora vamos falar um pouquinho sobre a classe LeituraRandom.

Ponto 1: ela instancia a classe SerialComLeitura, no entanto, apenas chama os métodos presentes nela, não chega a usá-la como uma thread de fato (o método run de SerialComLeitura nunca é chamado por LeituraRandom). Neste caso, é bobagem ter a classe SerialComLeitura extendendo a classe Thread. Deixe-a como uma classe padrão.

Lembre-se: se você instancia uma thread, está na realidade preparando por debaixo dos panos o seu sistema para a execução de um novo processo (isto lá no nível do sistema operacional). Consequentemente, está consumindo mais memória e recursos do sistema também.

Na mesma classe LeituraRandom, tenho também uma sugestão interessante para você: no método run há um loop que começa com a instrução
while(! isInterrupted())

pois bem: você pode também, de tempos em tempos pedir ao Java que libere memória acumulada pra você chamando o método System.gc();

Uma boa estratégia neste caso poderia ser a inclusão de um contador que, após a execução de n iterações do loop, chame este método. Algo como

int contador = 0;
while (! isInterrupted()) {
   contador++;
   if (contador % 1000 == 0) {
        System.gc();
   }
   //continuação do código
}

O que ocorre aqui: a cada 1000 iterações do loop, você estará instruindo o runtime do Java a iniciar o processo de coleta de lixo da memória. Porém, convém lembrar que esta coleta não é feita imediatamente. É a própria plataforma que decide quando o lixo será coletado. No entanto, chamando este método, você aumentará a prioridade de execução desta tarefa.

Note também que o número 1000 neste caso foi arbitrário e, importante: leve em consideração o fato de que chamando o método System.gc() irá diminuir um pouco a performance do seu sistema. Sendo assim, caso opte por utilizar esta estratégia, estude bem que tipo de valor trabalhar com o contador.

Ainda sobre a mesma estratégia, é importante lembrar que há um limite para o valor que pode ser armazenado em um inteiro. Se a sua aplicação for ser executada 24 horas por dia, convém colocar uma instrução dentro do seu loop como
if (contador > 100000) contador = 0;
evitando assim um erro de overflow com valores inteiros.

Estes foram alguns pontos que encontrei nas duas classes que você me enviou. Acredito que a primeira alteração, referente à inclusão do bloco finally no método getPeso da classe SerialComLeitura deverá ajudar significativamente no problema com a memória. No entanto, adotando todas estas dicas que lhe passei nesta consultoria, o resultado final poderá ser bem melhor do que o atual.

Espero que tenha lhe diso útil. Precisando de mais alguma coisa, estou a disposição.

Responder

Gostei + 0

Utilizamos cookies para fornecer uma melhor experiência para nossos usuários, consulte nossa política de privacidade.

Aceitar