簡體   English   中英

JFreeChart - Java 堆空間問題

[英]JFreeChart - Java Heap Space issue

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

我來自數據庫查詢的 reslutset 是 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;
}

在 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;

}

在我的數據庫類中執行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;
}

作為參考,我分析了兩個長時間運行的時間序列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

請嘗試從圖表中刪除工具提示和圖例(在構造函數中將它們設為“假”)。 它應該減少內存占用

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM