Использование памяти приложением GWT - PullRequest
2 голосов
/ 08 октября 2009

В настоящее время я работаю над приложением GWT в качестве доказательства технологии для будущих проектов. Мне нравится способ построения моего кода AJAX на Java вместо JavaScript. Но я, кажется, сталкиваюсь с проблемой памяти, когда я повторяю вызовы службы 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);
  }
}

EDIT:

Я потратил некоторое время, чтобы написать тестовое приложение, которое можно загрузить здесь . Я запустил приложение около получаса с включенным обновлением таблицы, после чего Firefox занял около 350 МБ памяти. Я также запустил тест с отключенной таблицей обновлений, поскольку использование Firefox в течение часа занимало чуть более 100 МБ.

(Для запуска этого примера вам понадобится API визуализации Google для GWT, который можно загрузить с 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());
}
}

Ответы [ 3 ]

1 голос
/ 10 октября 2009

Со всей дополнительной информацией, которую вы предоставили здесь, есть некоторые мысли. Я предполагаю, что увеличение памяти вызвано списками, остающимися в памяти. Возможно, что память вообще не освобождается, или сборщик мусора JavaScript не получает времени на очистку из-за слишком короткого промежутка времени между обновлениями. Вот несколько тестов, которые вы могли бы сделать:

  1. Чтобы проверить, не удастся ли сборщику мусора времени адаптировать ваш код так, чтобы обновление выполнялось только конечное число раз, а затем проверьте, не уменьшается ли использование памяти в течение нескольких минут. Если использование памяти уменьшается, то это может быть менее серьезной проблемой в вашем реальном примере. Но просто проверьте это, установив задержку на 30 секунд.
  2. Вы могли бы помочь сборщику мусора, очистив список после их использования: я не знаю, что лучше всего работает, поэтому вот несколько советов: удалите объект из списка или зациклите список и установите значения в ноль. Это не должно быть необходимым в нормальном случае, потому что сборщик мусора сделает это.

Вы также можете попробовать следующее дополнение к профилировщику памяти Firefox, чтобы узнать, можете ли вы увеличить объем памяти: http://ajaxian.com/archives/enhanced-firefox-memory-profiler-add-on

1 голос
/ 09 октября 2009

Я довольно давно использую GWT со многими таблицами и RPC, и до сих пор большинство утечек памяти, которые я обнаружил, были моей собственной ошибкой.

Насколько мне известно, уровень RPC не протекает, и ваш пример слишком прост, чтобы создать проблему.

Возможно, вам придется взглянуть на то, что на самом деле делает метод updateTable, возможно, у вас есть утечка в вашем собственном коде.

Одна вещь, которая может вызвать огромные утечки памяти с GWT, это Imagebundles в IE. Известно, что в GWT они сильно просачиваются, потому что они используют DXTransform для поддержки альфа-прозрачности. Память увеличивается большими кусками каждый раз, когда виджеты выводятся на экран. Но есть способы избежать этого.

0 голосов
/ 09 октября 2009

Нет, нет явной операции, необходимой для очистки мусора в Javascript. Предполагается, что он запускается автоматически (хотя сборщик мусора в браузере не на том же уровне, что и в современной JVM).

GWT делает все возможное, чтобы избежать распространенных ошибок, которые могут вызвать утечки памяти в JS (циклические ссылки между узлами JS и DOM плохо обрабатываются в некоторых браузерах).

Итак, вопрос в том, постоянно ли увеличивается использование памяти? Или он достигает максимума в определенный момент (или он просто падает с некоторым из нехватки памяти?). Может быть нормальным, что ваше приложение, кажется, растет и растет ... но GC должен сработать в какой-то момент.

В моем приложении использование памяти превышает 64 МБ. Но я не работаю над Ubuntu, IE на windows - наша главная цель, хотя я иногда тоже тестирую на FireFox (и там я тоже не вижу утечки).

Еще одна вещь, которая может вам понадобиться, - это избегать опроса каждые 2 секунды, как вы. Если запрос занимает более 2 секунд, вы начинаете очередь запросов (в браузере есть ограничение на количество одновременных подключений). поэтому лучше дождаться ответа, прежде чем запустить новый таймер.

...