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;
}
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.