Hi,
I'm currently working on a GWT application as a proof of technology for future projects. I like the way of building my AJAX code in Java instead of JavaScript. But I seem to be running into a memory problem when I repeat calls to an RPC service. The browser memory usage keeps growing and growing.
When searching Google I keep reading about how great GWT is and that its impossible to get memory leaks so can anyone explain why my browser (Firefox and Chromium) memory is rocketing?
Thanks in advance for helping me, Bram
The 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 to write a test application which can be downloaded here. I ran the application for about half an hour with the table update enabled after that Firefox took about 350MB of memory. I also ran the test with the update table disabled for an hour memory usage in Firefox went to little over 100MB.
(To run this sample you need the Google visualization API for GWT which can be downloaded from Google but I'm not allowed to post the link because of a new user policy )
I just got home from work and start another test without the table data update to see if memory usage keeps increasing or if it stops at a certain 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());
}
}