JavaFX,TreeTableView,RowFactory和内存泄漏

时间:2016-09-12 01:42:50

标签: javafx memory-leaks treetableview java-memory-leaks

我正在使用JavaFX TreeTableView来显示销售订单。父行包含“主”记录,子行包含有关订单的信息,包括订购的每个产品的行项目。我正在使用RowFactory根据其当前状态突出显示表中的行。 RowFactory的代码以此代码开头:

        currentOrderTree.setRowFactory(new     Callback<TreeTableView<MmTicket>, TreeTableRow<MmTicket>>() {
            @Override
            public TreeTableRow<MmTicket> call(TreeTableView<MmTicket> p) {
                final TreeTableRow<MmTicket> row = new TreeTableRow<MmTicket>() {
                    @Override
                    protected void updateItem(MmTicket order, boolean empty) {
                        super.updateItem(order, empty);
                        if (order != null) {
                System.out.println("Calling rowFactory method for the " + ++rowcall + " time.");
                            if (packingListPendingList.containsKey(order.getSono())) {
                                if (!getStyleClass().contains("pending")) {
                                    getStyleClass().remove("working");
                                    getStyleClass().remove("fulfilled");
                                    getStyleClass().remove("completed");
                                    getStyleClass().add("pending");
                                    getStyleClass().remove("shipped");
                                }
....

如您所见,每次调用行工厂时,Callback都会返回一个新的TreeTableRow。根据JavaFX 8.0文档,系统负责管理行的创建,并在适当时重用它们。 System.out.println调用记录了调用方法的次数。向上和向下滚动表格多次,以及由销售数据库更新引起的数据刷新,导致在相对较短的时间内调用此方法数万次。随着最终用户广泛使用TreeTableView,我在程序中的内存使用量持续增长。

对应用程序进行概要分析表明,HashTable和PsuedoClass(我认为是css的东西)以及诸如byte []和char []以及int []之类的内容正在使用大量内存。 我尝试过堆大小和垃圾收集器的不同组合,但最终结果总是相同的,因为应用程序最终会耗尽堆空间。

搜索答案表明有些人遇到了这个问题,而且我发现如果我没有进行任何行突出显示,从而没有调用行工厂,那么程序在管理内存方面要好得多。 任何人对可能导致此问题的原因或可能的解决方案有任何见解吗?

1 个答案:

答案 0 :(得分:1)

如果您跟踪正在创建的行对象的数量,您会发现并不多。显然,由于updateItem(...)被调用了很多次,因此发生了广泛的重用。

需要注意的一点是styleClass是作为列表实现的,当然这允许重复。因此,当您无法控制调用方法的时间以及传递给它的参数时,您需要非常小心地在updateItem(...)等方法中管理其内容。具体来说,您可能希望确保对updateItem(...)的调用没有可能的序列,这会导致将相同的值多次添加到样式类中。这包括order=null的调用。 (如果相同的单元格在用于空单元格,order=null和“挂起”单元格之间交替进行,会发生什么情况?)看起来您正在通过测试来检查({{1}但是有些角落的情况很容易被忽视。

尝试将if (! getStyleClass().contains("pending"))添加到调试输出中,以查看它是否过度增长。

无论如何,更干净的解决方案可能是使用CSS PseudoClasses而不是样式类。你可以按照

的方式做点什么
getStyleClass().size()

(或一些适当的逻辑)。 PseudoClasses只有两种状态(设置或未设置),因此这避免了管理样式类列表的所有问题。

在这种情况下你的CSS看起来像

PseudoClass pending = PseudoClass.getPseudoClass("pending");
PseudoClass working = PseudoClass.getPseudoClass("working");
PseudoClass fulfilled = PseudoClass.getPseudoClass("fulfilled");
PseudoClass completed = PseudoClass.getPseudoClass("completed");
PseudoClass shipped = PseudoClass.getPseudoClass("shipped");


@Override
protected void updateItem(MmTicket order, boolean empty) {
    super.updateItem(order, empty);

    pseudoClassStateChanged(pending, order != null && packingPendingList.containsKey(order.getSono()));
    pseudoClassStateChanged(working, order != null && packingWorkingList.containsKey(order.getSono()));
    pseudoClassStateChanged(fulfilled, order != null && packingFulfilledList.containsKey(order.getSono()));
    pseudoClassStateChanged(completed, order != null && packingCompletedList.containsKey(order.getSono()));
    pseudoClassStateChanged(shipped, order != null && packingShippedList.containsKey(order.getSono()));
}

它可能不适用于您的用例,但您也可以匹配具有多个PseudoClasses设置的节点

.table-row-cell {
    /* basic styles... */
}

.table-row-cell:pending {
    /* styles specific to pending items */
}

.table-row-cell:working {
    /* styles specific to working items */
}

/* etc etc */

如果你想坚持使用样式类,首先要确保处理null case。你可能想要像

这样的东西
.table-row-cell:pending:working { /* ... */ }

而不是

List<String> allStyles = Arrays.asList("pending", "working", "fulfilled", "completed", "shipped");

@Override
protected void updateItem(MmTicket order, boolean empty) {

    super.updateItem(order, empty);

    if (order == null) {
        getStyleClass().removeAll(allStyles);
    } else {
       // ...
    }
}

删除样式类列表中getStyleClass().remove("pending"); 第一次出现,考虑

"pending"

因为getStyleClass().removeAll(Collections.singletonList("pending")); 方法会删除所提供的集合中包含的所有元素

同样,这可能不是导致问题的原因,但如果您在removeAll(Collection)方法中操作它,则很容易让样式类列表无限增长。