设置各个行高时,JTable内存泄漏

问题描述 投票:0回答:1

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

为了拥有多行内容,我实现了自定义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中设置单个行高会造成大量内存泄漏。我是在做错什么,还是遇到了实际的错误?在后一种情况下,是否有任何已知的修复程序/解决方法?

我需要在JTable中显示多行内容。实际内容是在自定义模型中维护的对象的集合,该对象扩展了DefaultTableModel并通过...] >>

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

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

证明:以下代码解决了这个问题:

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

现在,行高只会增加,而永远不会减少。副作用:如果单元格内容发生变化,并且现在占用的行数少于以前,则行高不会发生变化以反映这一点。

底线:使用多行单元格呈现JTable是不平凡的,因此SO有很多错误示例。我发现的唯一有效示例是在https://www.javaspecialists.eu/archive/Issue106.html(由于另一个SO帖子而找到)。

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

这终于解决了内存泄漏/处理器消耗的问题,最后为我提供了一个如何正确计算单元格高度的实用示例。

java swing memory-leaks jtable
1个回答
0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.