2013-03-08 33 views
0

我第一次使用JFreeChart,并使用TimeSeriesCollection()创建TimeSeriesChart。JFreeChart - Java堆空间问题

我的数据库查询reslutset是应用程序。造成1000条记录。我正在使用org.jfree.date.time.Minute.Minute(int min .....)对象将其添加到TimeSeries对象。

我有一个JFrame,我可以直接添加ChartPanel。用户将提供新的输入参数并用新数据集重新加载图表数据。因此,我通过调用下面的方法

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

输出是完美的清理每重装之前。但我注意到,在Eclipse中多次运行该程序后,我看到下面有关Java堆空间的错误消息。有时候我也会在任务管理器中看到,即使数据集非常小(100条记录),程序仍会在PC内存中占据一席之地。

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) 

我的应用程序如下:

我有一个JFrame上,我直接传递一个表给它之后添加ChartPanel。

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

这里createDataset(从,到)是一种方法

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

getStuff被称为的SwingWorker线程内(DIBkgd法)

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; 

} 

在我的数据库类执行getDataDB

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; 
} 
+1

它可能只是不给java足够的负载所有的数据。尝试用-Xmx1024m – radai 2013-03-08 17:32:19

+1

我怀疑,要么你的代码,或在JFreeChart的一些代码不释放其产生的所有它的引用中的对象被锚定在内存,即使你期待他们GC'd运行。经过几个周期后,您的相当小的数据集将填充可用的堆并产生OutOfMemoryError。 Java分析器应该帮助确定什么是消耗内存来帮助您进行调试。 [VisualVM](http://docs.oracle.com/javase/6/docs/technotes/guides/visualvm/)包含在JDK中,可以帮助您收集这些信息。 – 2013-03-08 17:39:05

+1

使用探查器找出泄漏。注意'静态'变量,如果有的话。 – 2013-03-08 18:19:45

回答

1

我解决了我的问题。

我从@TrashGod处理线索来使用dispose()。但它不直接为我工作。

我是直接添加图表面板到我的主要JFrame容器。在我的情况下,我想继续在同一个JFrame容器中反复创建图表。

我第一次尝试清除数据集并在图表面板上调用removeall(),但它没有帮助。

然后,我发现的解决方案是创建另一个JFrame并添加图表面板。当我关闭此JFrame时,我再次清除数据集并在图表面板上调用removeall(),并且还调用dispose()。所以每次我创建一个新的图表,这个JFrame及其子组件都会创建并在我退出此JFrame时完全丢弃。

因此,创建了一个图表,当一个新的JFrame被创建,然后设置。

我还要补充一点,使这一变化,我开始看到了Java的VisualVM探查锯齿模式之后。我还使用Jprofiler,当我运行我的程序时,看到超过100,000个对象被创建,我感到震惊。现在,我看到创建了9000个对象,并且对于JFree包而言它保持不变,并基于检索到的结果集增加或减少数据库包中的对象数。

还有一件事我所做的是让我的SQL做解析,并将其转换为数字。我想减少创建的对象数量,并减少我的程序为每个检索到的记录完成的处理。

+0

这真让人大开眼界,不是吗?! – trashgod 2013-03-13 19:00:09

+0

@Trashgod ...的确如此! – user547453 2013-03-13 19:40:55

+0

我做的另一件事是让我的SQL进行解析并将其转换为数字。我想减少创建的对象数量,并减少我的程序为每个检索到的记录完成的处理。 – user547453 2013-03-13 19:47:51

3

作为参考,我分析了两个长时间运行时间系列DTSCTestMemoryUsageDemo。为了夸大规模,我使用了一个人工小堆,如下所示。在每种情况下,我都看到了周期性垃圾回收的典型锯齿形式返回基线,如here所示。相反,这种病理性example显示了不可恢复的资源消耗的内存的长期增加。

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

谢谢@Thrashgod .....你的评论指出我正确的方向。 – user547453 2013-03-13 18:22:32

+1

很高兴你把它分类。通常使用[单帧]更好(http://stackoverflow.com/q/9554636/230513)。更多泄漏候选人被提及[这里](http://stackoverflow.com/a/2486200/230513)。 – trashgod 2013-03-13 18:59:09

0

您的解决方案是伟大的! :))感谢你,我已经修复了我的堆溢出问题。但是,您的解决方案可能会更好。 :))在将图形绘制到面板上之前,只需调用方法panel.RemoveAll(); ,并且面板上的所有内容都将被丢弃。没有其他 JFrame 情况下是必要的...... 在我的情况下,解决办法是:

for(...) 
{ 

    panel.RemoveAll(); 

    drawData(listOfData); 

} 

有一个愉快的一天! :)

0

在该方法中org.jfree.chart.axis.DateAxis.refreshTicksHorizo​​ntal,我加入到成功避开了的OutOfMemoryError以下多余的线条。原因是在某些情况下,变量tickDate没有增加,所以“while(tickDate.before(upperDate))”的循环变成无限循环。

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