我目前正在研究GWT应用程序,作为未来项目的技术证明。我喜欢用Java而不是JavaScript构建我的AJAX代码的方式。但是当我重复调用RPC服务时,我似乎遇到了内存问题。浏览器内存使用量不断增长和增长。
在搜索Google时,我一直在阅读有关GWT有多棒以及无法获得内存泄漏的信息,所以有人可以解释为什么我的浏览器(Firefox和Chromium)内存会飙升吗?
先谢谢你的帮助, BRAM
代码:
...
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);
}
}
修改
我花了一些时间编写一个可以下载here的测试应用程序。在Firefox占用大约350MB内存之后启动了表更新,我运行了大约半小时的应用程序。我还运行了测试,禁用了更新表一小时,在Firefox中的内存使用率略高于100MB。
(要运行此示例,您需要GWT的Google可视化API,可以从Google下载,但由于新的用户政策,我不允许发布链接)
我刚下班回家,在没有表数据更新的情况下开始另一项测试,以查看内存使用量是否持续增加或者是否在某个时间点停止。
这是客户端实现类(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());
}
}
答案 0 :(得分:1)
我一直在使用GWT很多表和RPC,直到现在我发现大多数内存泄漏都是我自己的错。
据我所知,RPC层似乎没有泄漏,你的例子太简单了,无法解决问题。
您可能需要查看updateTable方法实际执行的操作,您可能在自己的代码中有泄漏。
使用GWT可能导致巨大内存泄漏的一件事是IE中的Imagebundles。众所周知,这些泄漏在GWT中非常严重,因为它们使用DXTransform来支持alpha透明度。每次在屏幕上放置小部件时,内存都会以大块的形式上升。但有一些技巧可以避免这种情况。
答案 1 :(得分:1)
您在此处提供的所有其他信息都是一些想法。我猜内存增加是由内存中剩余的列表引起的。可能内存根本没有释放,或者JavaScript垃圾收集器没有时间清理,因为更新之间的时间间隔也很短。以下是您可以做的一些测试:
您还可以尝试使用以下Firefox内存分析器插件来查看是否可以找到内存增加:http://ajaxian.com/archives/enhanced-firefox-memory-profiler-add-on
答案 2 :(得分:0)
在Javascript中没有清除垃圾所需的明确操作。它应该自动运行(尽管浏览器中的GC与现代JVM中的GC不同)。
GWT最好避免在JS中导致内存泄漏的常见陷阱(JS和DOM节点之间的循环引用在某些浏览器中处理不当)。
所以问题是:内存使用总是在增加吗?或者它是否在某个时刻达到顶峰(或者它只是因某些内存不足而崩溃?)。您的应用程序似乎正在增长并且正在增长,这是正常的......但GC应该在某个时候启动。
在我的应用程序中,内存使用量最高约为64MB。但是我没有在Ubuntu上工作,Windows上的IE是我们的主要目标,虽然我有时会在FireFox上测试(并且我也没有看到泄漏)。
您可能需要做的另一件事是避免像您一样每2秒轮询一次。如果请求的时间超过2秒,则开始排队请求(浏览器对并发连接数有限制)。所以最好在开始新的计时器之前等待响应。