JavaFX 滚动表更新性能随着时间的推移而降低

Posted

技术标签:

【中文标题】JavaFX 滚动表更新性能随着时间的推移而降低【英文标题】:JavaFX scrolling table update performance degrades over time 【发布时间】:2016-11-06 07:32:37 【问题描述】:

我有一个 TableView,它显示最后 N 个项目、顶部的新项目、从底部删除的项目等......似乎正在发生的事情是 CPU 负载随着时间的推移而增加,以至于同一台机器上的其他 X 应用程序变得迟缓。

平台详情:Redhat 6.7、32 位、Java 1.8u40

我尝试过的事情

引入 runLater() - 原始代码从非 FX 线程更新了可观察列表 - 显然这是错误的 优化 - 如果没有正在进行的更新,则仅在 JavaFX 应用程序线程上放置新的 Runnables 优化 - Observable 列表的批量更新,而不是单个添加 使用 jvisual VM 识别任何内存泄漏,找不到任何东西。 我试图重新创建这个 Windows 7(金属上)- JDK 8u40 64 位 => 不发生 Ubuntu 16.04 JDK 8u40 64 位(在带有 vmwgfx 的 VM 内)=> 不会发生 Ubuntu 16.04 OpenJDK + OpenJFX 最新版本 (8u91)(在金属上)=> 会发生

JVisual VM - 新硬件上的 Redhat 6u7(32 位)

JVisual VM - 旧硬件 (2008 iMac) 上的 Ubuntu 16.04(64 位)

此问题是较大应用程序的一部分,但我已将其隔离为下面的较小示例。这会使其他应用在几分钟后变得迟缓,但仅限于 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);
            );
        
    

问题

此问题与我更新表的方式或潜在错误有关吗? 还有更高效的表格更新方法吗?

【问题讨论】:

如果你要按顺序添加它,你可以在没有执行器服务的情况下完成它。而且您不需要同步块,因为列表本身已经同步 我已经在 CentOS 6.8 x86(安装了最新更新)和 Oracle 的 JDK 8u40 x86(在虚拟机中)进行了尝试。我对缓慢的其他应用程序没有任何问题。但是对于您的示例,我的堆大小最大为 45mb,而不像您的最大超过 100mb。 Windows 和 Ubuntu 16.04 的“金属”是否相同?你有什么显卡,安装了什么驱动程序,尤其是在 linux 上? Ubuntu 上的失败案例是在带有标准 radeon 驱动程序的“a01:00.0 VGA 兼容控制器:Advanced Micro Devices, Inc. [AMD/ATI] RV530/M56-P [Mobility Radeon X1600]”上。明天我会详细介绍 Redhat 机器(在办公室) java、ati 和 X 有一个“类似”的错误,但它是在一个非常旧的 Ubuntu 版本中,所以我认为这不再有效,但可能是一个很好的起点。也许您可以在其他带有英特尔或英伟达显卡的硬件上尝试相同的设置?bugs.launchpad.net/ubuntu/+source/sun-java6/+bug/250931 【参考方案1】:

尝试使用 G1GC 垃圾收集器。我有一个类似但不相同的问题(tableview 中存在大量持久对象),该问题已通过使用 G1GC 解决。

java XX:+UseG1GC

【讨论】:

以上是关于JavaFX 滚动表更新性能随着时间的推移而降低的主要内容,如果未能解决你的问题,请参考以下文章

一种有效的分位数算法/数据结构,允许样本随着时间的推移而更新?

随着时间的推移,使用熊猫计算滚动窗口中唯一 id 的特定条件事件的数量

CPU用久了,性能不会下降吗?

Docker Swarm滚动更新回滚服务

命令和查询责任分离 (CQRS) 模式

JavaFX - 如何从TextArea隐藏滚动条?