清除图表会导致内存泄漏?

时间:2014-11-12 19:47:58

标签: java multithreading memory-leaks

我试图用新数据每隔几秒重绘一次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

enter image description here

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

enter image description here

1 个答案:

答案 0 :(得分:1)

我发现删除所有系列的集合然后添加新的准备系列数据就是解决方案(bug?)。

这是我的新代码:

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

}

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

以下是内存分析: enter image description here

如您所见,所有应用程序的消耗都不超过11​​0 MB。

更新1:

事实上,上面的例子没有解决问题。在很多情况下会出现这种错误。我有很多小时测试并找到内存泄漏问题的最终解决方案。 唯一可能的是JFreeGhart中的#1111 bug。开放时间超过一年。 Mr. Gilber你可以解决这个问题吗?如果它能在短期内完成,我愿意为你付钱。

感谢。