Производительность обновления прокрутки таблицы JavaFX со временем ухудшается

У меня есть TableView, который показывает последние N элементов, новые элементы вверху, удаляет элементы снизу и т.д. То, что, похоже, происходит, - это загрузка ЦП с течением времени, чтобы указать, где другие X-приложения на одном компьютере становятся вялыми.

Подробная информация о платформе: Redhat 6.7, 32 бит, Java 1.8u40

Вещи, которые я пробовал

  • Введенный runLater() - исходный код обновил наблюдаемый список из потока non-FX - видимо, это неправильно
  • Оптимизация - помещайте новые Runnables в поток приложений JavaFX, если еще не выполняется обновление.
  • Оптимизировать -bulk обновления в списке наблюдаемых, а не отдельных добавляет
  • использовал виртуальную виртуальную машину для определения утечек памяти, не смог найти ничего.
  • Я пытался воссоздать это
    • Windows 7 (по металлу) - JDK 8u40 64 бит = > не встречается
    • Ubuntu 16.04 JDK 8u40 64 бит (внутри виртуальной машины с vmwgfx) = > не происходит
    • Ubuntu 16.04 OpenJDK + OpenJFX последний (8u91) (по металлу) = > выполняет

JVisual VM - Redhat 6u7 (32 бит) на новом оборудовании

Redhat 6u7

JVisual VM - Ubuntu 16.04 (64 бит) на старом оборудовании (2008 iMac)

Ubuntu 16.04 на Intel iMac

Эта проблема была частью более крупного приложения, но я выделил ее как меньший пример ниже. Это делает другие приложения вялыми через пару минут, но только на платформе Redhat 6u7.

public class TableUpdater extends Application {
    private int maxItems = 30;
    private AtomicBoolean pending = new AtomicBoolean();
    public class Thing {
        private String foo;
        public Thing(String foo) {
            this.foo = foo;
        }
        public String getFoo() {
            return foo;
        }
        @Override
        public int hashCode() {
            return foo.hashCode();
        }
        @Override
        public boolean equals(Object obj) {
            if (! (obj instanceof Thing)) {
                return false;
            }
            return ((Thing)obj).foo.equals(foo);
        }
    }

    public static void main(String[] args) {
        launch(args);
    }

    private int counter = 0;
    private ObservableList<Thing> data;
    private List<Thing> itemsToAdd = Collections.synchronizedList(new ArrayList<Thing>());

    @Override
    public void start(Stage primaryStage) throws Exception {
        TableView<Thing> table = new TableView<>();
        data = FXCollections.observableArrayList();
        table.setItems(data);

        TableColumn<Thing, String> fooCol = new TableColumn<>("Foo");
        fooCol.setCellValueFactory(new PropertyValueFactory<Thing, String>("foo"));
        table.getColumns().addAll(fooCol);

        Executors.newScheduledThreadPool(1).scheduleAtFixedRate(() -> {
            add(new Thing(String.format("%08d", counter++)));
        }, 0, 2, TimeUnit.MILLISECONDS);

        primaryStage.setScene(new Scene(table));
        primaryStage.setWidth(400);
        primaryStage.setHeight(400);
        primaryStage.show();
    }

    private void add(Thing thing) {
        itemsToAdd.add(thing);

        if (!pending.getAndSet(true)) {
            Platform.runLater(() -> {
                synchronized (itemsToAdd) {
                    Collections.reverse(itemsToAdd);
                    data.addAll(0, itemsToAdd);
                    itemsToAdd.clear();
                }
                if (data.size() > maxItems) {
                    data.remove(maxItems, data.size());
                }
                pending.set(false);
            });
        }
    }
}

Вопросы

  • Эта проблема связана с тем, как я обновляю таблицу или базовую ошибку?
  • Более эффективные способы обновления таблицы?

Ответ 1

Попробуйте использовать сборщик мусора G1GC. У меня была аналогичная, но не идентичная проблема (большое количество постоянных объектов в представлении таблицы), которая была решена с помощью G1GC.

java XX:+UseG1GC