【问题标题】:Clearing the chart causes memory leak?清除图表会导致内存泄漏?
【发布时间】:2014-11-12 19:47:57
【问题描述】:

我试图每隔几秒钟用新数据重绘一次 JFreeChart 系列。重绘是在自己的线程中进行的。一切正常,但清除系列会导致奇怪的内存泄漏。

我不确定问题是来自sleeping the thread 还是 clear() 方法中存在任何错误。

这是完整的示例代码(方法 SampleDataFeed 中的问题行):

import java.util.Calendar;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.time.FixedMillisecond;
import org.jfree.data.time.ohlc.OHLCSeries;
import org.jfree.data.time.ohlc.OHLCSeriesCollection;

public class sample1 {
    private static OHLCSeries series1;

    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                // window
                JFrame wnd = new JFrame();
                wnd.setVisible(true);
                wnd.setSize(800, 500);
                wnd.setLocationRelativeTo(null);
                wnd.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                // chart series
                OHLCSeries series = new OHLCSeries("Test");
                OHLCSeriesCollection seriesCollection = new OHLCSeriesCollection();
                seriesCollection.addSeries(series);
                series1 = seriesCollection.getSeries(0);

                // chart
                final JFreeChart chart = ChartFactory.createCandlestickChart(null, "Time", "Price", seriesCollection, false);
                chart.getXYPlot().setOrientation(PlotOrientation.VERTICAL);

                // chart panel
                final ChartPanel chartPanel = new ChartPanel(chart);
                chartPanel.setMaximumDrawHeight(2000);
                chartPanel.setMaximumDrawWidth(3000);
                wnd.add(chartPanel);

                // chart data feeding thread
                new DataFeedingThread().start();
            }
        });

    }

    private static class DataFeedingThread implements Runnable {
        private Thread t;

        @Override
        public void run() {

            // run recursively
            while (true) {

                // feed the chart with random data
                SampleDataFeed();

                // what 1 second before next run
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        // feed the chart with random data
        private void SampleDataFeed() {
            series1.clear(); // -----> This line causes memory leak! If I'd comment it, everything is Ok.
            for (int i = 0; i < 100; i++) {
                Calendar cal = Calendar.getInstance();
                cal.add(Calendar.MINUTE, i);
                FixedMillisecond fm = new FixedMillisecond(cal.getTime());
                series1.add(fm, randInt(95, 105), randInt(105, 110), randInt(90, 95), randInt(95, 105));
            }
        }

        // return random integer
        private int randInt(int min, int max) {
            Random rand = new Random();
            int randomNum = rand.nextInt((max - min) + 1) + min;
            return randomNum;
        }

        public void start() {
            if (t == null) {
                t = new Thread(this);
                t.start();
            }
        }
    }

}

更新:

JFreeChart 版本:1.0.19

编译器选项:无

内存使用不清除 ~15-30 MB

内存使用清除 ~430MB 并提高

【问题讨论】:

  • 我运行了您的示例程序,但它并没有耗尽内存。它的最大值约为 1.3G,但 GC 保持不变。
  • 1.3G太多了,你不觉得吗?
  • 可能很多,但不是经典意义上的泄漏。由于 GC 正在回收内存,因此您的堆可能太少了。我可以减少我的 MaxHeap 大小 (Xmx),但创建 100 个系列将有一个最低要求。
  • 嗯...对不起,我不明白你写的任何东西。 :(
  • “经典意义上的泄漏”意味着没有足够高的 -Xmx 值:堆将继续增长,直到达到极限。 @JohnVint 说 有一个足够高的限制,因此这不是内存泄漏,它只是您的程序需要多少内存。

标签: java multithreading memory-leaks


【解决方案1】:

我发现删除所有系列的集合,然后添加新的准备好的系列数据是解决这个问题的方法(错误?)。

这是我的新代码:

import java.util.Calendar;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.time.FixedMillisecond;
import org.jfree.data.time.ohlc.OHLCSeries;
import org.jfree.data.time.ohlc.OHLCSeriesCollection;

public class sample1 {
    private static final OHLCSeriesCollection seriesCollection = new OHLCSeriesCollection();
    private static final DataFeedingThread dataFeedingThread = new DataFeedingThread();

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                createAll();
            }
        });
    }

    public static void createAll() {
        // window
        JFrame wnd = new JFrame();
        wnd.setVisible(true);
        wnd.setSize(800, 500);
        wnd.setLocationRelativeTo(null);
        wnd.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // chart series
        seriesCollection.addSeries(new OHLCSeries("Test"));

        // chart
        final JFreeChart chart = ChartFactory.createCandlestickChart(null, "Time", "Price", seriesCollection, false);
        chart.getXYPlot().setOrientation(PlotOrientation.VERTICAL);

        // chart panel
        final ChartPanel chartPanel = new ChartPanel(chart);
        chartPanel.setMaximumDrawHeight(2000);
        chartPanel.setMaximumDrawWidth(3000);
        wnd.add(chartPanel);

        // chart data feeding thread
        dataFeedingThread.start();      
    }


    public static void setOHLCSeries(OHLCSeries series) {
        seriesCollection.removeAllSeries();
        seriesCollection.addSeries(series);
    }

    private static class DataFeedingThread implements Runnable {
        private Thread t;

        @Override
        public void run() {

            // run recursively
            while (true) {

                // feed the chart with random data
                SampleDataFeed();

                // what 1 second before next run
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        // feed the chart with random data
        private void SampleDataFeed() {
            OHLCSeries series = new OHLCSeries("test");
            for (int i = 0; i < 100; i++) {
                Calendar cal = Calendar.getInstance();
                cal.add(Calendar.MINUTE, i);
                FixedMillisecond fm = new FixedMillisecond(cal.getTime());
                series.add(fm, randInt(95, 105), randInt(105, 110), randInt(90, 95), randInt(95, 105));
            }
            setOHLCSeries(series);
        }

        // return random integer
        private int randInt(int min, int max) {
            Random rand = new Random();
            int randomNum = rand.nextInt((max - min) + 1) + min;
            return randomNum;
        }

        public void start() {
            if (t == null) {
                t = new Thread(this);
                t.start();
            }
        }
    }

}

在上面的代码中,我删除了图表中的所有系列并添加了新系列(带有准备好的数据)。这就是全部,一切正常。

这是内存分析:

如您所见,所有应用程序消耗的内存不超过 110 MB。

更新 1:

实际上上面的例子并没有解决问题。发生此错误的情况很多。我已经进行了许多小时的测试并找到了内存泄漏问题的最终解决方案。 唯一的一种可能性是这可以是 JFreeGhart 中的 #1111 bug。它的开放时间超过一年。 Mr. Gilber你能解决这个问题吗?如果它很快就能奏效,我愿意付钱给你。

谢谢。

【讨论】:

    猜你喜欢
    • 2015-04-14
    • 2021-06-14
    • 2021-03-23
    • 2021-09-25
    • 2014-12-08
    • 1970-01-01
    • 1970-01-01
    • 2010-09-09
    相关资源
    最近更新 更多