设置单个行高时 JTable 内存泄漏



我需要在JTable中显示多行内容。实际内容是在自定义模型中维护的对象集合,该模型通过覆盖getValueAt()来扩展DefaultTableModel并动态生成单元格内容。

为了拥有多行内容,我实现了一个自定义TableCellRenderer

private class MultiLineCellRenderer extends JTextArea implements TableCellRenderer {
public MultiLineCellRenderer() {
setLineWrap(true);
setWrapStyleWord(true);
setOpaque(true);
setBorder(new EmptyBorder(-1, 2, -1, 2));
setRows(1);
}
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
String text = value == null ? "" : value.toString();
if (!getText().equals(text)) {
setText(text);
int newHeight = table.getRowHeight() * getLineCount();
if (table.getRowHeight(row) != newHeight)
table.setRowHeight(row, newHeight);
}
if (isSelected) {
setForeground(table.getSelectionForeground());
setBackground(table.getSelectionBackground());
} else {
setForeground(table.getForeground());
setBackground(table.getBackground());
}
return this;
}
}

现在,如果我用几百行填充表(列计数为 2(,我会看到 AWT 工作线程开始最大化一个 CPU 内核。同时,内存消耗量从 ~100 MB 增加到该数量的十倍,甚至更多。即使应用程序实际上没有执行任何操作(后台未加载任何数据,没有用户交互(,并且仅在我清除表从中获取其内容的集合时才停止,也会发生这种情况。

通过注释掉选定的代码部分,我已将这些行确定为罪魁祸首:

int newHeight = table.getRowHeight() * getLineCount();
if (table.getRowHeight(row) != newHeight)
table.setRowHeight(row, newHeight);

如果我注释掉本节,所有表格行的高度都相同(1 行文本(,但内存消耗保持在 ~100 MB 左右。

如果我将这些行替换为对table.setRowHeight(row, 32)的单次调用,即使用固定值,内存消耗将无限期地再次开始上升。

以下修改有效,但代价是所有行具有相同的高度:

int newHeight = getRowHeight() * getLineCount();
if (table.getRowHeight() < newHeight)
table.setRowHeight(newHeight);

底线:似乎在 JTable 中设置单个行高会产生大量内存泄漏。我做错了什么,还是遇到了实际的错误?在后一种情况下,是否有任何已知的修复/解决方法?

设置行高会触发重绘,进而触发对渲染器的另一个调用。因此,重要的是,仅当行高与当前行高不同时才设置行高,以避免 endles 循环。当您无条件调用setRowHeight()时,即使使用固定值也是如此,也会发生这种情况。

第二个问题是每行包含两个单元格,这两个单元格可能具有不同的高度。上面的代码将设置行高以匹配现在呈现的单元格。当该行的另一个单元格呈现并具有不同的高度时,行高将再次更改。这将触发重绘,也是对行中第一列的重绘。由于这将导致另一个高度变化,因此再次出现精细循环。

证明:以下代码修复了此问题:

int newHeight = table.getRowHeight() * getLineCount();
if (table.getRowHeight(row) < newHeight)
table.setRowHeight(row, newHeight);

现在行高只会增加,但永远不会减少,从而打破无限循环。副作用:如果单元格内容更改并且现在占用的行比以前少,则行高不会更改以反映这一点。

底线:使用多行单元格渲染 JTable 并非易事,SO 有很多错误的例子。我找到的唯一工作示例(感谢另一个 SO 帖子(是 https://www.javaspecialists.eu/archive/Issue106.html。

他们的解决方案是在渲染器内部存储单元格高度(尽管这也可以在表模型中完成,以最适合您的实现为准(。计算单元格的高度时,请存储它,然后获取行中任何单元格的最大高度并使用它。此外,请确保仅当行高与当前行高不同时才设置行高。

这已经解决了内存泄漏/处理器消耗问题,此外还最终给了我一个如何正确计算单元格高度的工作示例。

最新更新