创建缩略图的正确方法是什么?

And*_* Sh 2 java swing thumbnails jfreechart jscrollpane

我正在尝试使用 using 创建一些数据的缩略图图表JScrollPane,但遇到性能困难。此示例有大约 100 个缩略图,每个缩略图有 5000 个样本。当我尝试向下滚动并返回向上多次时,滚动会出现延迟,CPU 负载增加,应用程序内存使用量达到超过 500 Mb。

有没有办法在不减少数据的情况下避免此性能问题?

在此输入图像描述

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.ThermometerPlot;
import org.jfree.data.general.DefaultValueDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

public class ThumbnailChartsTest extends JPanel {
private static final int W = 200;
private static final int H = W;
private static final int N = 5000;
private static final Random random = new Random();

private static ChartPanel createPane() {
    final XYSeries series = new XYSeries("Data");
    for (int i = 0; i < random.nextInt(N) + N; i++) {
        series.add(i, random.nextGaussian());
    }
    XYSeriesCollection dataset = new XYSeriesCollection(series);

    JFreeChart chart = ChartFactory.createXYLineChart("Random", "Domain",
        "Range", dataset, PlotOrientation.VERTICAL, false, false, false);
    return new ChartPanel(chart, W, H, W, H, W, H,
            false, true, true, true, true, true);
}

public static void main(final String[] args) {

    EventQueue.invokeLater(new Runnable() {

        @Override
        public void run() {
            JFrame f = new JFrame("Test");
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            JPanel panel = new JPanel();
            panel.setLayout(new GridLayout(0, 4));
            for (int i=0; i<100; i++){
                panel.add(createPane());
            }

            JScrollPane scrollPane = new JScrollPane(panel,
                    JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
                    JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
            f.add(scrollPane);

            f.pack();
            f.setVisible(true);
        }
    });

}
}
Run Code Online (Sandbox Code Playgroud)

编辑:我不明白一件事:为什么在这种情况下内存使用量仍然非常巨大!请看这个插图。

图像

补充: 我觉得这里面有一些误解。

通过监控 VisualVM 的堆大小 在此输入图像描述 启动applet后堆大小只有125 Mb,很酷。但随后我开始测试:多次滚动和调整大小,越来越多——上下,上下,更小的框架和更大的框架。堆大小增长超过 500 Mb!我想这种情况并不正常。

添加#2

现实世界的例子:

我的数据大小约为 2 Mb,以 90 个图表表示(每个图表 2 个系列),一个系列包含 3000 个元素。我已经实现了通过滑块更改数字列。 在此输入图像描述

但对于这个小数据堆大小增长超过 1.5 GB!

在此输入图像描述

这种情况发生在一些操作之后,更改数字列,例如对于我的 CPU(核心 2 双核 2.2GHz),每个绘图表需要大约 4 秒的时间!由于延迟很大,很难控制滑块。

更新:

我已将数据下采样到每个缩略图 100 个样本。现在它肯定更快了,但是巨大的堆大小问题仍然存在。图片上是700Mb以上,而且还不是记录。我很沮丧。 在此输入图像描述

tra*_*god 5

使用享元模式仅渲染可见图表。此处概述了JTable 渲染器使用的方法,并如下所示。为了便于说明,每次显示单元格时都会重新创建数据集;滚动、调整大小和切换应用程序以查看效果。虽然这种渲染可以很好地扩展到数万个单元格,但每个图表仍然渲染数据点。您可以在方法的实现中限制可见单元格的数量,如下所示。ChartRendererNScrollablegetPreferredScrollableViewportSize()

如何将内存使用量减少到很小的值?

没有通用的答案,但有几种策略可能会有所帮助:

  • 尽可能在程序初始化时编写图表,而不是在渲染图表时;下面更新的示例构造了一个TableModel实例ChartPanel;相应地ChartRenderer就更简单了。

  • 超过几千个点的图表实际上是不可读的;考虑截断大型数据集并仅在响应 a 时显示完整数据ListSelectionEvent如下所示。

  • 平台活动监控器可能会产生误导;配置文件以验证实际结果。

图像

启动小程序后,它并不大,不到 200 Mb,但在滚动、调整大小等之后,内存使用量达到了超过 600 Mb 的值。为什么?

下面代码的典型分析器视图仅显示中等使用情况以及滚动和垃圾收集后的预期空闲/已用比率;您的结果可能会有所不同。

轮廓

import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

/**
 * @see /sf/answers/2831160111/
 */
public class ChartTable {

    private static final Random R = new Random();
    private static final int N = 5000;
    private static final int W = 200;
    private static final int H = W;

    private void display() {
        JFrame f = new JFrame("ChartTable");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        DefaultTableModel model = new DefaultTableModel(
            new String[]{"", "", "", ""}, 0) {
            @Override
            public Class<?> getColumnClass(int columnIndex) {
                return ChartPanel.class;
            }
        };
        for (int r = 0; r < 25; r++) {
            ChartPanel[] row = new ChartPanel[4];
            for (int c = 0; c < row.length; c++) {
                final XYSeries series = new XYSeries("Data");
                int n = R.nextInt(N);
                for (int i = 0; i < n; i++) {
                    series.add(i, R.nextGaussian());
                }
                XYSeriesCollection dataset = new XYSeriesCollection(series);
                JFreeChart chart = ChartFactory.createXYLineChart(
                    "Random " + series.getItemCount(), "Domain", "Range", dataset);
                ChartPanel chartPanel = new ChartPanel(chart) {
                    @Override
                    public Dimension getPreferredSize() {
                        return new Dimension(W, H);
                    }
                };
                row[c] = chartPanel;
            }
            model.addRow(row);
        }
        JTable table = new JTable(model) {
            @Override
            public Dimension getPreferredScrollableViewportSize() {
                return new Dimension(4 * W, 2 * H);
            }
        };
        table.setDefaultRenderer(ChartPanel.class, new ChartRenderer());
        table.setRowHeight(W);
        f.add(new JScrollPane(table));
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    private static class ChartRenderer implements TableCellRenderer {

        @Override
        public Component getTableCellRendererComponent(
            JTable table, Object value, boolean isSelected,
            boolean hasFocus, int row, int column) {
            return (ChartPanel) value;
        }
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new ChartTable()::display);
    }
}
Run Code Online (Sandbox Code Playgroud)