Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I'm trying to create a chart of thumbnails of some data with using JScrollPane, but I encounter performance difficulties. This example has about 100 thumbnails charts with 5000 samples in each one. When I'm trying to scroll down and back to up multiple times, scrolling occurs with delay, CPU load increasing, application memory usage reaches over 500 Mb.

Is there a way to avoid this performance problem without reducing my data?

enter image description here

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);
        }
    });

}
}

Edit: I can't understand one thing: Why in this case memory usage still very huge! Please look at this illustration.

image

Addition: I think there is some misunderstanding.

Heap size by monitor visualVM enter image description here After starting applet heap size is only 125 Mb, it's cool. But then I'm starting testing: scrolling and resizing multiple times, more and more -- up and down, up and down, smaller frame and bigger frame. Heap size growing up over 500 Mb! I suppose this situation isn't normal.

Addition #2

Real-world example:

My data has size about only 2 Mb and represented in 90 charts(2 series in each one), one series contain 3000 elements. I've implemented changing number columns by slider. enter image description here

But for this small data heap size growing up over 1.5 GB!

enter image description here

This happens after some actions, changing number columns e.g. For my CPU(core 2 duo 2.2GHz) every drawing table takes time about 4 sec! With this big delay it's hard to control slider.

update:

I've implemented downsampling my data to 100 samples per thumbnail chart. Now It's definitely faster, but problem with HUGE heap size still there. On the picture the one is above 700Mb, and it's not a record. I'm frustrated. enter image description here

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
161 views
Welcome To Ask or Share your Answers For Others

1 Answer

Use the flyweight pattern to render only visible charts. The approach, used by JTable renderers, is outlined here and shown in the ChartRenderer seen below. For illustration, the dataset is recreated each time a cell is revealed; scroll, resize and switch applications to see the effect. While such rendering scales well into the tens of thousands of cells, each chart still renders N data points. You can limit the number of visible cells in the implementation of the Scrollable method, getPreferredScrollableViewportSize(), as shown below.

How to reduce memory usage to small values?

There is no general answer, but several strategies may prove helpful:

  • Compose charts as early in program initialization as possible, rather than at the time they are rendered; the updated example below constructs a TableModel of ChartPanel instances; the ChartRenderer is correspondingly simpler.

  • Charts having more than a few thousand points are effectively unreadable; consider truncating large datasets and displaying the full data only in response to a ListSelectionEvent, illustrated here.

  • Platform activity monitors may be misleading; profile to verify actual results.

image

After starting applet it's not big, less than 200 Mb, but after scrolling, resizing etc. memory usage reaches values more than 600 Mb. Why?

A typical profiler view of the code below shows only moderate usage and the expected free/used ratio after scrolling and garbage collection; your results may vary.

profile

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 https://stackoverflow.com/a/40445144/230513
 */
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);
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...