2

私は現在、将来のプロジェクトのテクノロジーの証明としてGWTアプリケーションに取り組んでいます。JavaScriptではなくJavaでAJAXコードを作成する方法が好きです。しかし、RPCサービスへの呼び出しを繰り返すと、メモリの問題が発生しているようです。ブラウザのメモリ使用量は増え続けています。

Googleを検索するとき、GWTがどれほど優れているか、メモリリークを取得することは不可能であるということを読み続けているので、ブラウザ(FirefoxとChromium)のメモリが急増している理由を誰かが説明できますか?

助けてくれてありがとう、ブラム

コード:

...

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

編集:

ここからダウンロードできるテストアプリケーションを作成するのに少し時間を費やしました。Firefoxが約350MBのメモリを使用した後、テーブルの更新を有効にしてアプリケーションを約30分間実行しました。また、Firefoxのメモリ使用量が100MBを少し超えるまで、更新テーブルを無効にしてテストを実行しました。

(このサンプルを実行するには、GoogleからダウンロードできるGWT用のGoogle視覚化APIが必要ですが、新しいユーザーポリシーのため、リンクを投稿することはできません)

仕事から家に帰って、テーブルデータを更新せずに別のテストを開始して、メモリ使用量が増え続けるかどうか、または特定の時点で停止するかどうかを確認しました。

これは、クライアント実装クラス(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());
}
}
4

3 に答える 3

1

ここで提供したすべての追加情報とともに、いくつかの考えがあります。メモリの増加は、リストがメモリに残っていることが原因だと思います。更新間の時間枠が短すぎるために、メモリがまったく解放されていないか、JavaScriptガベージコレクターがクリーンアップする時間がない可能性があります。実行できるテストは次のとおりです。

  1. ガベージコレクターが時間を取得しないかどうかをテストするには、更新が有限回数だけ実行されるようにコードを調整してから、メモリ使用量が数分で減少するかどうかを確認します。メモリ使用量が減少した場合、実際の例ではそれほど問題にはならない可能性があります。ただし、遅延を30秒に設定して、簡単にテストしてください。
  2. ガベージコレクターは、使用後にリストをクリアすることで支援できます。何が最適かわからないため、いくつかの提案があります。リストからオブジェクトを削除するか、リストをループして値をnullに設定します。ガベージコレクターが行うので、これは通常の場合は必要ありません。

次のFirefoxメモリプロファイラーアドオンを試して、メモリの増加を見つけることができるかどうかを確認することもできます:http: //ajaxian.com/archives/enhanced-firefox-memory-profiler-add-on

于 2009-10-10T09:32:44.317 に答える
1

私はかなり長い間GWTを多くのテーブルとRPCで使用してきましたが、これまで私が見つけたほとんどのメモリリークは私自身のせいでした。

私の知る限り、RPCレイヤーはリークしていないようです。あなたの例は単純すぎて、問題を引き起こすことはできません。

updateTableメソッドが実際に何をしているのかを調べる必要があるかもしれません。あなた自身のコードにリークがあるかもしれません。

GWTで巨大なメモリリークを引き起こす可能性のあるものの1つは、IEのImagebundlesです。アルファ透過性をサポートするためにDXTransformを使用しているため、これらがGWTで極端にリークすることは既知の事実です。ウィジェットが画面に表示されるたびに、メモリは大きなチャンクで増加します。しかし、これを回避するためのトリックがあります。

于 2009-10-09T07:28:23.480 に答える
0

Javascriptでガベージをクリーンアップするために必要な明示的な操作はありません。自動的に実行されることになっています(ただし、ブラウザーのGCは最新のJVMと同じレベルではありません)。

GWTは、JSでメモリリークを引き起こす可能性のある一般的な落とし穴を回避するために最善を尽くします(一部のブラウザーでは、JSノードとDOMノード間の循環参照が適切に処理されません)。

したがって、問題は次のとおりです。メモリ使用量は常に増加していますか?または、特定の時点でトップになりますか(または、メモリ不足でクラッシュしますか?)。アプリケーションが成長しているように見えるのは正常な場合があります...しかし、GCはある時点で開始する必要があります。

私のアプリケーションでは、メモリ使用量は約64MBで最大になります。しかし、私はUbuntuで作業していません。私は、FireFoxをウェルとしてテストすることもありますが、Windows上のIEが主なターゲットです(リークも見られません)。

あなたがしなければならないかもしれないもう一つのことはあなたがするように2秒ごとにポーリングするのを避けることです。リクエストに2秒以上かかる場合は、リクエストのキューイングを開始します(ブラウザには同時接続の数に制限があります)。したがって、新しいタイマーを起動する前に応答を待つのが最善です。

于 2009-10-09T12:11:21.670 に答える