Description
Describe the bug
XChart
seems to be using a larger amount of memory than expected, i.e. compared to JFreeChart
.
To Reproduce
I coded two simple examples, where clicking a button adds X number of data points to an XY chart (i.e. X=18000 to represent one sample per second for 5 hours). One example uses XChart
and the other uses JFreeChart
.
I used VisualVm
to monitor the heap usage before and after adding the data points. I included the screenshots below. For each test, I clicked the button to add the data 5 times, each click roughly 10 seconds apart.
XChart Example
package org.example;
import org.knowm.xchart.QuickChart;
import org.knowm.xchart.XChartPanel;
import org.knowm.xchart.XYChart;
import javax.swing.JButton;
import javax.swing.JFrame;
import java.awt.Color;
public class SingleShotExampleXChart {
public static final int NUM_DATA_POINTS = 18000;
public static void main(String[] args) {
double[] xData = new double[NUM_DATA_POINTS];
double[] yData = new double[NUM_DATA_POINTS];
final XYChart chart = QuickChart.getChart("Simple XChart Demo", "Time (ms)", "Y", "mockData", xData, yData);
XChartPanel<XYChart> chartPanel = new XChartPanel<>(chart);
JFrame frame = new JFrame("XChart Example");
long timestamp = System.currentTimeMillis();
for (int i = 0; i < NUM_DATA_POINTS; i++) {
yData[i] = Math.random() * 100;
xData[i] = timestamp + i;
}
JButton button = new JButton("Add New Data");
button.addActionListener(e -> {
Color randomColor = new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255));
chart.getSeriesMap().get("mockData").setMarkerColor(randomColor);
chart.getSeriesMap().get("mockData").setLineColor(randomColor);
chart.getSeriesMap().get("mockData").setFillColor(randomColor);
javax.swing.SwingUtilities.invokeLater(() -> {
chart.updateXYSeries("mockData", xData, yData, null);
chartPanel.repaint();
});
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new java.awt.BorderLayout());
frame.add(button, java.awt.BorderLayout.SOUTH);
frame.add(chartPanel, java.awt.BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
}
JFreeChart Example
package org.example;
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.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import javax.swing.JButton;
import javax.swing.JFrame;
import java.awt.BorderLayout;
import java.awt.Color;
public class SingleShotJFreeChart {
public static final int NUM_DATA_POINTS = 18000;
public static void main(String[] args) {
double[] xData = new double[NUM_DATA_POINTS];
double[] yData = new double[NUM_DATA_POINTS];
long timestamp = System.currentTimeMillis();
for (int i = 0; i < NUM_DATA_POINTS; i++) {
yData[i] = Math.random() * 100;
xData[i] = timestamp + i;
}
XYSeries series = new XYSeries("mockData");
XYSeriesCollection dataset = new XYSeriesCollection(series);
JFreeChart chart = ChartFactory.createXYLineChart(
"Simple JFreeChart Demo",
"Time (ms)",
"Y",
dataset,
PlotOrientation.VERTICAL,
true,
true,
false
);
XYPlot plot = chart.getXYPlot();
XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(true, false);
plot.setRenderer(renderer);
ChartPanel chartPanel = new ChartPanel(chart);
JFrame frame = new JFrame("JFreeChart Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
JButton button = new JButton("Add New Data");
button.addActionListener(e -> {
Color randomColor = new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255));
renderer.setSeriesPaint(0, randomColor);
plot.setRenderer(renderer);
javax.swing.SwingUtilities.invokeLater(() -> {
series.clear();
for (int i = 0; i < NUM_DATA_POINTS; i++) {
series.add(xData[i], yData[i]);
}
chartPanel.repaint();
});
});
frame.add(button, BorderLayout.SOUTH);
frame.add(chartPanel, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
}
Screenshots
XChart
JFreeChart
Expected behavior
You can see from the screenshots, XChart
is using several times more memory than JFreeChart
: up to 1.25 GB at the peak. Eventually, it looks like garbage collection kicks in, but even at this point XChart
is using around 210 MB while JFreeChart is around 42 MB. Note, if I manually trigger garbage collection, the memory usage in XChart
drops to negligible levels (~10 MB)
It seems unexpected for XChart
to be using that amount of memory; ideally less memory would be needed.
Thank you!