Pergunta

Atualmente, estou trabalhando em um aplicativo GWT como prova de tecnologia para projetos futuros. Gosto da maneira de construir meu código Ajax em Java em vez de JavaScript. Mas pareço estar enfrentando um problema de memória quando repito chamadas para um serviço RPC. O uso da memória do navegador continua crescendo e crescendo.

Ao pesquisar no Google, continuo lendo sobre o quão grande é o GWT e que é impossível obter vazamentos de memória, então alguém pode explicar por que meu navegador (Firefox e Chromium) a memória está disparando?

Agradecemos antecipadamente por me ajudar, Bram

O código:

...

class ExampleTable extends Composite

  private RPCService rpcService;
  private Timer tableUpdater;

  public ExampleTable(){
   ... Init timer and RPC Service
   ... Add components
   initWidget();
  }

  private void getTableDataFromRPCService() {
  this.rpcService.getData(new AsyncCallback<ArrayList<Data>>() {

      @Override
      public void onSuccess(ArrayList<Data> result) {
          ExampleTable.this.updateTable(result);
      }

      @Override
      public void onFailure(Throwable caught) {
          //Do nothing
      }
  });
  }

  private void updateTable(ArrayList<Data> tableData){
    ... Update the table
  }

  private void startUpdateTask() {
      this.serviceUpdater = new Timer() {
          @Override
          public void run() {
            ExampleTable.this.getTableDataFromRPCService();
          }
      };
      serviceUpdater.scheduleRepeating(2000);
  }
}

EDITAR:

Passei algum tempo para escrever um aplicativo de teste que pode ser baixado aqui. Eu executei o aplicativo por cerca de meia hora com a atualização da tabela ativada depois que o Firefox levou cerca de 350 MB de memória. Também executei o teste com a tabela de atualização desativada para um uso de memória de hora no Firefox, foi para pouco mais de 100 MB.

(Para executar esta amostra, você precisa da API de visualização do Google para GWT, que pode ser baixada do Google, mas não tenho permissão para postar o link por causa de uma nova política de usuário)

Acabei de chegar em casa do trabalho e iniciar outro teste sem a atualização dos dados da tabela para verificar se o uso da memória continua aumentando ou se ele para em um determinado ponto.

Esta é a classe de implementação do cliente (gwtmemoryissue.java):

public class GWTMemoryIssue implements EntryPoint {
//Run with or without table
private static final boolean WITH_TABLE = false; 

private final TestServiceAsync rpcService = GWT.create(TestService.class);

private Panel panel;
private Timer timer;
private Table table;

public void onModuleLoad() {
    RootPanel rootPanel = RootPanel.get();

    this.panel = new VerticalPanel();
    this.panel.setSize("100%", "100%");

    rootPanel.add(panel);

    if (WITH_TABLE) {
        loadTable();
    }else{
        startUpdateTask();
    }

}

private void startUpdateTask() {
    this.timer = new Timer() {

        @Override
        public void run() {
            GWTMemoryIssue.this.getTableData();

        }
    };
    this.timer.scheduleRepeating(2000);
}

public void loadTable() {
    Runnable onLoadCallback = new Runnable() {
        public void run() {
            GWTMemoryIssue.this.table = new Table(createTableData(), createTableOptions());
            GWTMemoryIssue.this.table.setSize("100%", "100%");
            GWTMemoryIssue.this.panel.add(GWTMemoryIssue.this.table);
            GWTMemoryIssue.this.startUpdateTask();
        }
    };

    VisualizationUtils.loadVisualizationApi(onLoadCallback, Table.PACKAGE);
}

private Options createTableOptions() {
    Options options = Options.create();

    return options;
}

private DataTable createTableData() {
    DataTable data = DataTable.create();

    data.addColumn(ColumnType.STRING, "Name");
    data.addColumn(ColumnType.NUMBER, "Intval 1");
    data.addColumn(ColumnType.NUMBER, "Intval 2");
    data.addColumn(ColumnType.NUMBER, "Intval 3");

    return data;
}

private void getTableData() {
    rpcService.getListOfItems(new AsyncCallback<ArrayList<ListItem>>(){
        public void onFailure(Throwable caught) {
            // Do nothing
        }

        public void onSuccess(ArrayList<ListItem> result) {
            if (WITH_TABLE){
                GWTMemoryIssue.this.updateTableData(result);
            }else{
                //Ignore the data from the server
            }
        }
    });
}

private void updateTableData(ArrayList<ListItem> result) {
    DataTable data = createTableData();

    data.addRows(result.size());

    int row = 0;
    for (ListItem li : result) {
        data.setValue(row, 0, li.getName());
        data.setValue(row, 1, li.getIntVal());
        data.setValue(row, 2, li.getIntSecondVal());
        data.setValue(row, 3, li.getThirdIntVal());
        row++;
    }

    this.table.draw(data, createTableOptions());
}
}
Foi útil?

Solução

Com todas as informações adicionais que você forneceu aqui estão alguns pensamentos. Eu acho que o aumento da memória é causado pelas listas restantes na memória. É possível que a memória não seja libertada ou o coletor de lixo JavaScript não tem tempo para limpar, devido também ao curto prazo entre as atualizações. Aqui estão alguns testes que você pode fazer:

  1. Para testar se o coletor de lixo não ganha tempo adaptando seu código, de modo que a atualização execute apenas um número finito de vezes e verifique se o uso da memória diminui por alguns minutos. Se o uso da memória diminuir, pode ser menos um problema no seu exemplo do mundo real. Mas o simples teste definindo o atraso para os 30 segundos.
  2. Você pode ajudar o coletor de lixo limpando a lista depois que eles são usados: não sei o que funciona melhor, então aqui estão algumas sugestões: remova o objeto da lista ou faça um loop sobre a lista e defina valores como nulos. Isso não deve ser necessário no caso normal, porque o coletor de lixo o faria.

Você também pode tentar o seguinte complemento Firefox Memory Profiler para ver se você pode localizar o aumento da memória: http://ajaxian.com/archives/enhanced-firefox-memory-profiler-add-on

Outras dicas

Eu tenho usado o GWT há algum tempo com muitas tabelas e RPCs e até agora a maioria dos vazamentos de memória que encontrei foi minha culpa.

A camada RPC não parece vazar até onde eu sei e seu exemplo é simples demais para representar um problema.

Pode ser necessário dar uma olhada no que o método Updatetable está realmente fazendo, você pode ter um vazamento em seu próprio código.

Uma coisa que pode causar grandes vazamentos de memória com o GWT são o ImageBundles no IE. É um fato conhecido que estes vazam extremamente no GWT porque estão usando o DXtransform para suportar a transparência da alfa. A memória sobe em grandes pedaços sempre que os widgets são colocados na tela. Mas há truques para evitar isso.

Não, não há operação explícita necessária para limpar o lixo em JavaScript. Deveria ser executado automaticamente (embora o GC no navegador não esteja no mesmo nível que em uma JVM moderna).

O GWT faz melhor evitar armadilhas comuns que causariam vazamentos de memória em JS (as referências circulares entre os nós JS e DOM são mal tratadas em alguns navegadores).

Portanto, a pergunta é: o uso da memória está sempre subindo? Ou ele chega a um determinado ponto (ou apenas trava com alguns fora da memória?). Pode ser normal que seu aplicativo pareça estar crescendo e crescendo ... mas o GC deve começar em algum momento.

Na minha memória de aplicativo, o uso de memória é de cerca de 64 MB. Mas não estou trabalhando no Ubuntu, ou seja, no Windows, é nosso principal alvo, embora às vezes eu teste o Firefox como bem -vindo (e lá também não vejo vazamento).

Outra coisa que você precisa fazer é evitar pesquisas a cada 2 segundos como você. Se uma solicitação levar mais de 2 segundos, você inicia a fila das solicitações (um navegador tem uma limitação para o número de conexões simultâneas). Portanto, é melhor esperar a resposta antes de disparar um novo cronômetro.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top