JFreeChart – Java堆空间问题

我第一次使用JFreeChart,我使用TimeSeriesCollection()来创建TimeSeriesChart。

我在数据库查询中的重新设置是应用程序。 记录了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堆空间的错误消息。 有时我也会在任务管理器中看到程序在PC内存中占用,即使数据集非常小(100条记录)。

 Exception occurred during event dispatching: java.lang.OutOfMemoryError: Java heap space at sun.util.calendar.Gregorian.newCalendarDate(Gregorian.java:67) at java.util.GregorianCalendar.(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,我在将Chart传递给它后直接添加了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(from,to)是一个方法

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

在SwingWorker线程中调用getStuff (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() { @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; } 

在我的Database类中,执行getDataDB

  public List 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; } 

作为参考,我描述了两个长时间运行的时间序列DTSCTestMemoryUsageDemo 。 为了夸大比例,我使用了一个人为的小堆,如下所示。 在每种情况下,我都看到周期性垃圾收集的典型锯齿模式返回到基线,如此处所示。 相比之下,这个病态的例子表明,不可恢复的资源消耗了大量的内存。

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

我解决了我的问题。

我从@TrashGod中获取了使用dispose()的线索。 但它并不直接对我有用。

我将图表面板直接添加到我的主JFrame容器中。 在我的情况下,我想一遍又一遍地在同一个JFrame容器中创建图表。

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

然后我找到的解决方案是创建另一个JFrame并将图表面板添加到它。 当我关闭这个JFrame时,我再次清除数据集并在图表面板上调用removeall()并调用dispose()。 因此,每次我创建一个新图表时,都会创建此JFrame及其子组件,并在退出此JFrame时完全处理。

因此,在创建图表时,会创建一个新的JFrame,然后进行处理。

我还应该补充说,在进行此更改后,我开始在Java VisualVM分析器中看到Saw Tooth模式。 我还使用了Jprofiler,当我运行程序时,我惊讶地看到创建了超过100,000个对象。 现在,我看到创建了9000个对象,它对于JFree包保持不变,并且根据检索到的结果集,我的数据库包中的对象数量增加或减少。

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

你的解决方案很棒! :))多亏了你,我已经修复了我的堆溢出问题。 但是,您的解决方案可以更好。 :))在将图形绘制到面板上之前,只需调用方法panel.RemoveAll(); 以及您面板上之前的所有内容都将被处理掉。 没有其他JFrame实例是必要的…在我的情况下,解决方案是:

 for(...) { panel.RemoveAll(); drawData(listOfData); } 

祝你今天愉快! 🙂

在方法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