Pregunta

Actualmente estoy trabajando en una aplicación GWT como una prueba de tecnología para proyectos futuros. Me gusta la forma de construir mi código AJAX en Java en lugar de JavaScript. Pero me parece estar corriendo en un problema de memoria cuando repito llamadas a un servicio RPC. El uso de la memoria del navegador sigue creciendo y creciendo.

Durante la búsqueda Google sigo leyendo acerca de cuán grande es GWT y que su imposible conseguir pérdidas de memoria de modo que nadie puede explicar por qué mi navegador (Firefox y cromo) de memoria se está disparando?

Gracias de antemano por ayudarme, Bram

El 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);
  }
}

EDIT:

He pasado algún tiempo para escribir una aplicación de prueba que se puede descargar aquí . Me encontré con la solicitud de una media hora con la actualización de la tabla habilitada después de que Firefox tomó alrededor de 350 MB de memoria. También corrió la prueba con la tabla de actualización desactivada para un uso de memoria en Firefox horas fue a poco más de 100 MB.

(Para ejecutar este ejemplo se necesita el API de visualización de Google para GWT que puede ser descargado de Google, pero no estoy autorizado a enviar el enlace a causa de una nueva política de usuario)

Me acaba de llegar del trabajo y empezar otra prueba sin la actualización de datos de la tabla para ver si el uso de memoria sigue aumentando o si se detiene en un punto determinado.

Esta es la clase de implementación de 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());
}
}
¿Fue útil?

Solución

Con toda la información adicional que proporcionan a continuación son algunas ideas. Supongo que el aumento de la memoria es causada por las listas restantes en la memoria. Es bien posible que la memoria no se libera en absoluto o el recolector de basura de JavaScript no llega el momento de limpiar, debido también al corto período de tiempo entre las actualizaciones. Éstos son algunos de pruebas que podría hacer:

  1. Para probar si el recolector de basura no consigue adaptarse tiempo se codifican de manera que la actualización sólo se ejecuta un número finito de veces, y luego comprobar si el uso de la memoria disminuye en unos pocos minutos. Si el uso de la memoria disminuye, lo que podría ser un problema menor en ti ejemplo del mundo real. Pero simple prueba mediante el establecimiento de la demora a los 30 segundos.
  2. Usted podría ayudar al recolector de basura en la limpieza de la lista después de que se utilizan: No sé lo que funciona mejor, así que aquí están algunas sugerencias: Retire el objeto de la lista, o bucle sobre la lista y establecer los valores a null. Esto no debería ser necesario en caso normal, debido a que el recolector de basura lo haría.

También puede probar el generador de perfiles de memoria siguientes complemento de Firefox para ver si se puede localizar a los aumentos de memoria: http://ajaxian.com/archives/enhanced-firefox-memory-profiler-add-on

Otros consejos

He estado usando GWT bastante tiempo con muchas mesas y RPC y hasta ahora la mayoría de las pérdidas de memoria que encontré fueron mi culpa.

No parece que la capa de RPC a la fuga por lo que yo sé y tu ejemplo es demasiado simple para plantear un problema.

Es posible que tenga que tomar un vistazo a lo que el método updateTable está haciendo realidad, es posible que tenga una fuga en su propio código.

Una cosa que puede causar grandes pérdidas de memoria con GWT son Imagebundles en IE. Es un hecho conocido que éstos se escapan muy en GWT porque están utilizando DXTransform para apoyar la transparencia alfa. La memoria sube en grandes trozos de widgets everytime se ponen en la pantalla. Pero hay trucos para evitar esto.

No hay ninguna operación explícita necesario para limpiar la basura en Javascript. Se supone que se ejecute automáticamente (aunque el GC en el navegador no está en el mismo nivel que en una JVM moderna).

GWT lo hace posible para evitar los errores comunes que podrían causar pérdidas de memoria en JS (referencias circulares entre los nodos y JS DOM están mal manejados en algunos navegadores).

Así que la pregunta es: es el uso de la memoria siempre subiendo? ¿O comienzo a cabo en un momento determinado (o simplemente se estrella con un poco fuera de la memoria?). Puede ser normal que su aplicación parece estar creciendo y creciendo ... pero GC debe poner en algún momento.

En mi uso de la memoria de aplicación alcanza un máximo de aproximadamente 64 MB. Pero no estoy trabajando en Ubuntu, IE en las ventanas es nuestro principal objetivo, aunque a veces prueba en Firefox como wel (y no veo una fuga tampoco).

Otra cosa que puede que tenga que hacer es evitar el sondeo cada 2 segundos como lo hace. Si una solicitud tarda más de 2 segundos de empezar a hacer fila a las solicitudes (un navegador tiene una limitación en el número de conexiones simultáneas). así que es mejor que esperar a la respuesta antes de disparar un nuevo temporizador.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top