Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

large memory usage #879

Open
flavius-t opened this issue Aug 19, 2024 · 3 comments
Open

large memory usage #879

flavius-t opened this issue Aug 19, 2024 · 3 comments

Comments

@flavius-t
Copy link

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

image

JFreeChart

image

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)

image

It seems unexpected for XChart to be using that amount of memory; ideally less memory would be needed.

Thank you!

@A1trdX
Copy link

A1trdX commented Oct 12, 2024

I was unable to reproduce your problem. May be different GC, JDK 21 uses G1GC by default.

Environment:

  • Windows 10
  • Oracle JDK 21.0.2 2024-01-16 LTS
  • XChart 3.8.8
  • JFreeChart 1.5.5

JVM Flags: -Xmx2G

XChart

image

JFreeChart

image

@A1trdX
Copy link

A1trdX commented Oct 12, 2024

With -Xmx64M (I didn't notice major differences in performance):

image

@flavius-t
Copy link
Author

@A1trdX Thanks for the reply. My apologies, I realize I forgot to include the environment I was using:

  • Windows 11
  • OpenLogic OpenJDK 1.8.0_392-392-b08
  • XChart 3.8.7
  • JFreeChart 1.5.5

I ran the examples from above again:

image
image

I tried with the JVM flags as well, first Xmx64M and then Xmx2G:

image
image

I also repeated the test with no JVM flags, with OpenLogic OpenJDK 21.0.3.9. The heap usage does seem similar this time:

image
image

I added the -XX:+UseG1GC flag with JDK 1.8 and the results are much better:
image

I suppose the issue could indeed come from JDK 1.8's GC, which is Parallel GC by default.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants