GWT application memory usage

I am currently working on a GWT application as proof of technology for future projects. I like the way I create my AJAX code in Java instead of JavaScript. But I seem to be running into a memory issue when I repeat calls to the RPC service. Browser memory usage continues to grow and grow.

When I google I keep reading about how big GWT is and that it cannot be leaked, so can anyone explain why my browser's memory is torn (Firefox and Chromium)?

Thanks in advance for your help, Bram

Code:

...

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:

I've spent some time creating a test application that can be downloaded here . I ran the app for about half an hour with a table refresh after Firefox took about 350MB of memory. I also ran a test with the refresh table disabled for hour memory usage in Firefox down to just over 100MB.

(To run this example, you need the Google Visualization API for GWT, which can be downloaded from Google, but I am not allowed to post the link due to new user policy)

I just got back from work and started a new test without updating the table data to see if the memory usage is increasing or if it stops at some point.

This is the client implementation class (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());
}
}

      

+2


source to share


3 answers


With all the additional information you provided here, there are some thoughts. I think the increase in memory is caused by the list left in memory. It is either possible that the memory is not freed at all, or the JavaScript garbage collector does not get time to clean up, because the time interval between updates is too short. Here are some tests you could do:

  • To check if the garbage collector is collecting, you use code such that the update only occurs a finite number of times, and then check to see if the memory usage decreases over a few minutes. If memory usage decreases, this may be less of a problem in your real world example. But try testing it by setting the delay to 30 seconds.
  • You can help the garbage collector by clearing the list after they have been used: I don't know which works best, so here are some tips: remove an object from the list, or iterate over the list and set the values ​​to null. This is not necessary in the normal case, because the garbage collector will do it.


You can also try the following Firefox Profiler Add-on to see if memory can be increased: http://ajaxian.com/archives/enhanced-firefox-memory-profiler-add-on p>

+1


source


I've been using GWT for quite a while with many tables and RPCs, and so far most of the memory leaks I've found were my own mistake.

The RPC layer doesn't seem to flow as far as I know and your example is too simple to create a problem.



You may need to take a look at what the updateTable method does, perhaps you have a leak in your own code.

One thing that can cause huge memory leaks with GWT is Imagebundles in IE. These leaks are known to occur exclusively in GWT because they use DXTransform to support alpha transparency. Memory goes up in large chunks every time widgets are placed on the screen. But there are tricks to avoid this.

+1


source


There is no explicit operation required to clear the garbage in Javascript. It should start automatically (although the GC in the browser is not at the same level as in the modern JVM).

GWT is better off avoiding common mistakes that could cause memory leaks in JS (circular references between JS and DOM nodes don't handle well in some browsers).

So the question is, is memory being used constantly? Or does it end at some point (or does it just crash with some memory?). It might be ok that your application seems to be growing and growing ... but the GC should come at some point.

In my application, the memory usage reaches around 64MB. But I'm not working on Ubuntu, IE on Windows is our main target, although sometimes I test FireFox (and I don't see a leak there either).

Another thing you might want is to avoid polling every 2 seconds like you do. If the request takes more than 2 seconds, you start trying out requests (the browser has a limit on the number of concurrent connections). so it is best to wait for a response before starting a new timer.

0


source







All Articles