0

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

我从数据库查询中得到的结果是 app。大约 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,在将 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;
}

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;

}

在我的 Database 类中执行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;
}
4

5 回答 5

3

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

$ java -Xms32m -Xmx80m -cp build/classes:dist/lib/* chart.DTSCTest
$ java -Xms32m -Xmx80m -jar jfreechart-1.0.14-demo.jar
于 2013-03-09T04:45:53.987 回答
1

我解决了我的问题。

我从@TrashGod 那里得到了使用 dispose() 的线索。但它并不直接为我工作。

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

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

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

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

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

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

于 2013-03-13T18:13:23.207 回答
0

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

for(...)
{

    panel.RemoveAll();

    drawData(listOfData);

}

祝你今天过得愉快!:)

于 2013-06-25T15:06:29.980 回答
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
于 2013-07-10T16:44:26.870 回答
0

请尝试从图表中删除工具提示和图例(在构造函数中将它们设为“假”)。它应该减少内存占用

于 2020-01-29T16:14:20.317 回答