我正在使用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 []之类的内容正在使用大量内存。 我尝试过堆大小和垃圾收集器的不同组合,但最终结果总是相同的,因为应用程序最终会耗尽堆空间。
搜索答案表明有些人遇到了这个问题,而且我发现如果我没有进行任何行突出显示,从而没有调用行工厂,那么程序在管理内存方面要好得多。 任何人对可能导致此问题的原因或可能的解决方案有任何见解吗?
答案 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)
方法中操作它,则很容易让样式类列表无限增长。