2013-03-08 98 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