首页 文章

JFreeChart - Java堆空间问题

提问于
浏览
0

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

我在数据库查询中的重新设置是应用程序 . 超过1000条记录 . 我正在使用org.jfree.date.time.Minute.Minute(int min .....)对象将其添加到TimeSeries对象中 .

我有一个JFrame,我直接添加ChartPanel . 用户将提供新的输入参数并使用新数据集重新加载图表数据 . 所以我在每个 reload 之前通过在方法中调用以下内容来清理

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

在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<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 回答

  • 3

    作为参考,我描述了两个长时间运行时间系列DTSCTestMemoryUsageDemo . 为了夸大比例,我使用了一个人为的小堆,如下所示 . 在每种情况下,我都看到了典型的定期垃圾收集的锯齿模式返回基线,如图所示here . 相比之下,这种病态显示了不可恢复资源消耗记忆的长期增长 .

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

    我解决了我的问题 .

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

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

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

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

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

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

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

  • 0

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

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

    祝你今天愉快! :)

  • 1

    在方法org.jfree.chart.axis.DateAxis.refreshTicksHorizontal中,我添加了以下额外的行来成功避免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
    

相关问题