0
votes

I am using JFreeChart for the first time and I am using a TimeSeriesCollection() to create a TimeSeriesChart.

My reslutset from the DB query is app. aroung 1000 records. I am using org.jfree.date.time.Minute.Minute(int min.....) object to add it to a TimeSeries object.

I have a JFrame on which I add the ChartPanel directly. The user will provide new input parameters and reload the chart data with new dataset. So I clean up before every reload by calling the following in a method

            dataset.removeAllSeries();
            chart.removeLegend();
            chart.getRenderingHints().clear();
            cp.getChartRenderingInfo().setEntityCollection(null);
            cp.removeAll();
            cp.revalidate();

The output is perfect. But I noticed that after running the program 'several times in Eclipse' I see the below error message about Java heap space. Sometimes I also see in the Task Manager that the program hogs on the PC memory even though the dataset is very small (100 records).

Exception occurred during event dispatching:
java.lang.OutOfMemoryError: Java heap space
at sun.util.calendar.Gregorian.newCalendarDate(Gregorian.java:67)
at java.util.GregorianCalendar.<init>(GregorianCalendar.java:575)
at java.util.Calendar.createCalendar(Calendar.java:1012)
at java.util.Calendar.getInstance(Calendar.java:964)
at org.jfree.chart.axis.DateTickUnit.addToDate(DateTickUnit.java:238)
at org.jfree.chart.axis.DateAxis.refreshTicksHorizontal(DateAxis.java:1685)
at org.jfree.chart.axis.DateAxis.refreshTicks(DateAxis.java:1556)
at org.jfree.chart.axis.ValueAxis.reserveSpace(ValueAxis.java:809)
at org.jfree.chart.plot.XYPlot.calculateDomainAxisSpace(XYPlot.java:3119)
at org.jfree.chart.plot.XYPlot.calculateAxisSpace(XYPlot.java:3077)
at org.jfree.chart.plot.XYPlot.draw(XYPlot.java:3220)
at org.jfree.chart.JFreeChart.draw(JFreeChart.java:1237)
at org.jfree.chart.ChartPanel.paintComponent(ChartPanel.java:1677)
at javax.swing.JComponent.paint(JComponent.java:1029)
at javax.swing.JComponent.paintToOffscreen(JComponent.java:5124)
at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1491)
at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1422)
at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:294)
at javax.swing.RepaintManager.paint(RepaintManager.java:1225)
at javax.swing.JComponent._paintImmediately(JComponent.java:5072)
at javax.swing.JComponent.paintImmediately(JComponent.java:4882)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:786)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:714)
at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:694)
at javax.swing.RepaintManager.access$700(RepaintManager.java:41)
at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1636)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:646)
at java.awt.EventQueue.access$000(EventQueue.java:84)
at java.awt.EventQueue$1.run(EventQueue.java:607)
at java.awt.EventQueue$1.run(EventQueue.java:605)
at java.security.AccessController.doPrivileged(Native Method)

My application is as follows:

I have a JFrame on which I directly add the ChartPanel after passing a Chart to it.

chart = ChartFactory.createTimeSeriesChart("Peak monitor", , "Time: Zoom in", "# of Requests Logged", createDataset(from,to), true, false, false);

            chartpanel = new ChartPanel(chart);

            FramePanel.this.add(cp);


            validate();

Here createDataset(from, to) is a method

 private TimeSeriesCollection createDataset(Date from, Date to) {
    dataset.addSeries(controller.getStuff(from, to));
    return dataset;
}

getStuff is called within a SwingWorker thread (DIBkgd method)

 public TimeSeries getStuff(Date from, Date to) {
    s1 = new TimeSeries("Log Requests");

    final Date from1 = from;
    final Date to1 = to;

    progressDialog.setVisible(true);

    sw = new SwingWorker<Void, Integer>() {

        @Override
        protected Void doInBackground() throws Exception {

            if (db.getCon() == null) {
                db.connect();
            }
            Arrlst2.clear();
            Arrlst2= db.getDataDB(from1, to1);

            for (Qryobjects x : Arrlst2) {                  
              s1.add(new Minute(x.getMinute(), x.getHour(), x.getDay(), x.getMonth(), x.getYear()), x.getCount());
            }

            System.out.println("finished fetching data");
            return null;
        }

        @Override
        protected void done() {
            progressDialog.setVisible(false);
        }
    };
    sw.execute();
    return s1;

}

Within my Database class the getDataDB is executed:

 public List<Qryobjects> getDataDB(Date from, Date to) {

    PreparedStatement select;
    ResultSet rs;

    String selectSql = "Select Sum(Cnt) Cid, Hr, Min, Dat from (Select count(H.Request_Id) Cnt , To_Char(H.Timestamp,'HH24') HR, To_Char(H.Timestamp,'mm') MIN, To_Char(H.Timestamp,'MM-dd-yyyy') DAT From Status_History H Where H.Timestamp Between ? And ? Group By  H.Request_Id,  H.Timestamp Order By H.Timestamp Asc) Group By Hr, Min, Dat order by Dat asc";

    try {
        select = con.prepareStatement(selectSql);

        select.setDate(1, from);
        select.setDate(2, to);

        rs = select.executeQuery();

        System.setProperty("true", "true");

        while (rs.next()) {

            int cnt = rs.getInt("cid");

            int hour = Integer.parseInt(rs.getString("Hr"));
            int min = Integer.parseInt(rs.getString("Min"));
            int month = Integer.parseInt(rs.getString("dat").substring(0, 2));
             int day = Integer.parseInt(rs.getString("dat").substring(3, 5));
            int year = Integer.parseInt(rs.getString("dat").substring(6, 10));

             Arrlst1.add(new Qryobjects(cnt, hour, min, day, month,year));

        }
        rs.close();

    } catch (SQLException e) {
        e.printStackTrace();
    }

    return Arrlst1;
}
5
its possible youre simply not giving java enough heap to load all the data. try running with -Xmx1024mradai
I suspect that either your code, or some code in JFreeChart is not freeing all it's references which is resulting in objects being anchored in memory even though you expect them to be GC'd. After a few cycles, your fairly small data set fills the available heap and produces a OutOfMemoryError. A java profiler should help identify what is consuming memory to aid you in debugging. VisualVM is included with the JDK and can help you gather this information.Jason Braucht
Use a profiler to find out the leak. Beware of static variables, if any.Guillaume Polet
@ radai, Jason Braucht and Guillaume Polet.......Thank you so much for telling me about the VMProfiler. I noticed my max Heap Size was 256MB and I could see the max capacity exceeding. I also tried something different and this time, I tried freeing up the references for primitive types "int" because I was actually creating new int type variables inside the while loop. And boy...it did make a difference (so far.....). I tried to increase the JVM Heap Size from command prompt as well from Java Control Panel and I still do not see the effect in the Java VisualVM.user547453
also...I do not understand although the heap size is say like 15 MB my CP memory usage in Task Manager says 50 MB and keeps gradually increasing every time I reload the charts.user547453

5 Answers

3
votes

For reference, I profiled two long running time series DTSCTest and MemoryUsageDemo. To exaggerate the scale, I used an artificially small heap, as shown below. In each case, I saw the typical saw-tooth pattern of periodic garbage collection return to baseline, as shown here. In contrast, this pathological example shows a secular rise in consumed memory from unrecoverable resources.

$ java -Xms32m -Xmx80m -cp build/classes:dist/lib/* chart.DTSCTest
$ java -Xms32m -Xmx80m -jar jfreechart-1.0.14-demo.jar
1
votes

I resolved my problem.

I took the clue from @TrashGod to use dispose(). But it does not work directly for me.

I was adding the chart panel directly to my main JFrame container. And in my case, I wanted to keep creating the charts in the same JFrame container over and over.

I first tried clearing the dataset and called removeall() on the chart panel, but it did not help.

Then the solution I found was to create another JFrame and add the chart panel to it. And when I closed this JFrame, I again clear the dataset and called removeall() on the chart panel and also called dispose(). So everytime, I create a new chart, this JFrame and its children componenets are created and are completely disposed when I exit this JFrame.

So, when a chart is created a new JFrame is created and then disposed.

I should also add that after making this change I started to see the Saw Tooth pattern in the Java VisualVM profiler. I also used Jprofiler and I was shocked to see more than 100,000 objects were created while I was running my program. Now, I see 9000 objects created and it remains constant for the JFree package and based on my resultset retrieved the number of objects in my database package increases or decreases.

One more thing I did was to make my SQL do the parsing and convert it to a number. I wanted to reduce the number of objects created and also reduce the processing done by my program for each retrieved record.

0
votes

Your solution is great! :)) Thanks to you, I have fixed my heap overflow problem. But, your solution can be even better. :)) Before you draw your graph onto panel, just call method panel.RemoveAll(); and everything that was before on your panel will be disposed. No other JFrame instances are necessary... In my case, solution was:

for(...)
{

    panel.RemoveAll();

    drawData(listOfData);

}

Have a nice day! :)

0
votes

In the method org.jfree.chart.axis.DateAxis.refreshTicksHorizontal, I added following extra lines to successfully avoided the OutOfmemoryError. the reason is for some circumstance, the variable tickDate is not increased, so the loop of "while (tickDate.before(upperDate))" becomes an infinite loop.

protected List refreshTicksHorizontal(Graphics2D g2,
            Rectangle2D dataArea, RectangleEdge edge) {

    List result = new java.util.ArrayList();

    Font tickLabelFont = getTickLabelFont();
    g2.setFont(tickLabelFont);

    if (isAutoTickUnitSelection()) {
        selectAutoTickUnit(g2, dataArea, edge);
    }

    DateTickUnit unit = getTickUnit();
    Date tickDate = calculateLowestVisibleTickValue(unit);
    Date upperDate = getMaximumDate();

    boolean hasRolled = false;
    Date previousTickDate=null;            //added 
    while (tickDate.before(upperDate)) {
        if(previousTickDate!=null && tickDate.getTime()<=previousTickDate.getTime()){  //added 
            tickDate=new Date(tickDate.getTime()+100L); //added 
        }  //added 
        previousTickDate=tickDate; //added 
        //System.out.println("tickDate="+tickDate+" upperDate="+upperDate);**  //add to see infinite loop
0
votes

Please try by removing the tooltips and the legend from the chart (making them 'false' in the constructor). It should reduce the memory footprint