Problema com consumo de memória do JFreeChart

5 respostas
X

Olá pessoal.

Tenho uma aplicação com 8 gráficos com dados sendo inseridos dinamicamente em um loop infinito. O problema é que o consumo de memória vai aumentando até dar um erro de falta de memória. Abaixo tem um exemplo simples do código de gráfico dinâmico do próprio JFreeChart com o problema de consumo.

Como vcs podem ver, eu configurei series.removeAgedItems(true) e estou invocando o garbage collector a cada 500 passos. Apesar disso, o problema persiste.

Alguma dica?

Obrigado.

import java.awt.BorderLayout;   
import java.awt.Dimension;   
import java.awt.event.ActionEvent;   
import java.awt.event.ActionListener;   
import java.io.PrintStream;   
import javax.swing.*;   
import org.jfree.chart.*;   
import org.jfree.chart.axis.ValueAxis;   
import org.jfree.chart.plot.XYPlot;   
import org.jfree.data.time.*;   
import org.jfree.data.xy.XYDataset;   
import org.jfree.ui.ApplicationFrame;   
import org.jfree.ui.RefineryUtilities;   
   
public class DynamicDataDemo1 extends ApplicationFrame   
{   

    static class DemoPanel extends JPanel   
        implements ActionListener   
    {   
   
        private TimeSeries series;   
        private double lastValue;   
   
        private JFreeChart createChart(XYDataset xydataset)   
        {   
            JFreeChart jfreechart = ChartFactory.createTimeSeriesChart("Dynamic Data Demo", "Time", "Value", xydataset, true, true, false);   
            XYPlot xyplot = jfreechart.getXYPlot();   
            ValueAxis valueaxis = xyplot.getDomainAxis();   
            valueaxis.setAutoRange(true);   
            valueaxis.setFixedAutoRange(30000D);  // in milliseconds
            valueaxis = xyplot.getRangeAxis();   
            valueaxis.setRange(0.0D, 200D);   
            return jfreechart;   
        }   
   
        public void actionPerformed(ActionEvent actionevent)   
        {   

        }   
      
        public DemoPanel()   
        {   
            super(new BorderLayout());   
            lastValue = 100D;   
            series = new TimeSeries("Random Data", Millisecond.class);   
            TimeSeriesCollection timeseriescollection = new TimeSeriesCollection(series);   
            ChartPanel chartpanel = new ChartPanel(createChart(timeseriescollection), false);
            chartpanel.setPreferredSize(new Dimension(500, 270));   
            JPanel jpanel = new JPanel();   
            jpanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));   
      
            add(chartpanel);   
            add(jpanel, "South");            
        }   
      
      public void start () {

         int count=0;
         double d;
         Millisecond millisecond;
      
         series.removeAgedItems(true);      
         
         do {
         
                d = 0.90000000000000002D + 0.20000000000000001D * Math.random();
                lastValue = lastValue * d;   
                millisecond = new Millisecond();   
                //System.out.println("Now = " + millisecond.toString());   
                series.add(new Millisecond(), lastValue);   

                    count++;

                    if (count > 500) {
                           System.out.println("CLEANNING MEMORY!!!!!!!");
                           System.gc();
                           System.runFinalization();
                           System.out.println("DONE!!!!!!!");
                           count = 0;
                    }
            
            try {
               Thread.sleep(10);
            } catch (Exception e) {}
            
         } while (lastValue > 0);
         
      }
    }   
   
   
    static Class class$org$jfree$data$time$Millisecond; /* synthetic field */   
   private DemoPanel demopanel;   
   
    public DynamicDataDemo1(String s)   
    {   
        super(s);   
        //DemoPanel 
      demopanel = new DemoPanel();   
        setContentPane(demopanel);         
    }   
   
   public void start (){
      demopanel.start();
   }
   
    public static JPanel createDemoPanel()   
    {   
        return new DemoPanel();   
    }   
   
    public static void main(String args[])   
    {   
        DynamicDataDemo1 dynamicdatademo1 = new DynamicDataDemo1("Dynamic Data Demo");   
        dynamicdatademo1.pack();   
        RefineryUtilities.centerFrameOnScreen(dynamicdatademo1);   
        dynamicdatademo1.setVisible(true);   
      dynamicdatademo1.start();
    }   
   
}

5 Respostas

BrunoBastosPJ

Em primeiro lugar, chamar o System.gc nunca é uma boa ideia… A JVM já sabe a melhor hora de chamar, sugerir que o gc seja ativado apenas faz você gastar recursos de sua máquina percorrendo o heap em busca de lixo em um momento desnecessário…

Seu problema está em:

series.add(new Millisecond(), lastValue);

A instancia de Millisecond nunca ficará elegível ao gc quando series existir!

Obs: seu código está muito doido :wink:

X

Olá Bruno.

Chamar o gc foi uma tentativa de limpar a memória, mas eu já havia lido sobre isso que vc disse.

Mudei o código que vc colocou. Na realidade, deveria ser

series.add(millisecond, lastValue);

visto que millisecond já havia sido criado na linha de cima.

Contudo, o erro ainda persiste. O interessante é que o código inicia ocupando cerca de 17mb. Iniciei o código usando -Xmx10m e não fez nenhuma diferença no gc.

E então? Alguma dica?

Obs.: esse código é do demo do JFreeChart.

BrunoBastosPJ

instancie millisecond fora do loop.

X

Olá Bruno.

Depois de muito custo, consegui arrumar o loop como vc sugeriu:

public void start () {

			double d;

			Date date = new Date();
			date.setTime(System.currentTimeMillis());

			series.removeAgedItems(true);		
			
			do {
			
                d = 0.90000000000000002D + 0.20000000000000001D * Math.random();
                lastValue = lastValue * d;   
                
                series.add(RegularTimePeriod.createInstance(Millisecond.class, date, RegularTimePeriod.DEFAULT_TIME_ZONE), lastValue);   				
				
				try {
					Thread.sleep(10);
					
					date.setTime(System.currentTimeMillis());
					
				} catch (Exception e) {}
				
			} while (lastValue > 0);
			
		}

Tirei o new Millisecond de dentro do loop e substitui pelo RegularTimePeriod.createInstance, que retorna o objeto necessário para ser adicionado à série. Pra conseguir isso, criei um objeto date fora do loop que é atualizado dentro do loop usando o setTime.

O resultado no consumo de memória foi o mesmo.

Alguma outra sugestão?

BrunoBastosPJ

O método createInstance como o nome já diz, retorna uma nova instância. não muda nada do que você tinha feito antes…

Como disse você tem que instanciar o milliseconds fora do loop e não dentro dele, como está fazendo :wink:

Criado 26 de setembro de 2010
Ultima resposta 28 de set. de 2010
Respostas 5
Participantes 2