Excessive CPU Usage

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
tony

Excessive CPU Usage

Post by tony » Fri Sep 06, 2002 7:36 pm

Hello Dave.

I have a problem with the TimeSeriesCollection ultimately consuming 100% processor time and effectively slowing down the computer and distorting the plot. It takes about 10 minutes for the CPU usage to reach 100%, and it never comes down.

I have included a modified demo class to illustrate this problem. There are 2 comment sections in the class that explain how to duplicate and demonstrate it the problem. ( It's not a problem, it's a feature, right ;-) )

My suspicion is that there is a problem (garbage collecting issue) adding a new Millisecond class to the TimeSeries every time it is called from the accessor methods. Is there another way to do this? I basically dissected the code in the example classes and made this OScope work for me.

PS - I hope this posts well.


//*************** Begin OScope class ***************

import java.text.DecimalFormat;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;

import com.jrefinery.data.*;
import com.jrefinery.chart.*;
import com.jrefinery.chart.entity.*;
import com.jrefinery.chart.event.*;
import com.jrefinery.chart.tooltips.*;
import com.jrefinery.chart.ui.*;

/**
* A simple class demonstrating run-away CPU usage with the BasicTimeSeries or
* TimeSeriesCollection classes (or other class related to a Dynamic XYPlot).
*
* This application was built and tested using Java 1.4.0 although it may work
* using Java 1.3.x. Additionally, this demo requires the classes in the
* JCommon and JFreeChart distributions.
*
* See the comment section below above the Accessor methods for further
* explaination of the run-away CPU usage problem and a way to demonstrate
* the problem.
*
* If anyone has any questions, comments or needs further explaination, please
* call or email.
*
* thank you,
* Tony Bianchini
* Tony.Bianchini@aei.com
* 970-407-6136
*/
public class OScope extends JLayeredPane {

private static final Font BUTTON_FONT = new Font("SansSerif", Font.PLAIN, 12);
private ChartPanel chartPnl;
private JFreeChart chart;
private XYSeriesCollection dataset;
private XYDataset data;
private XYPlot xyplot;
private VerticalNumberAxis range;
private HorizontalDateAxis domain;

private BasicTimeSeries cyanTimeSeries, greenTimeSeries;
private TimeSeriesCollection timeDataset;

private DecimalFormat df = new DecimalFormat("#.00");
private BasicStroke gridStroke, seriesStroke;
private JLabel cyanLabel, greenLabel;


public OScope() {
super();

NumberTickUnit verticalTicks = new NumberTickUnit(25.0, new DecimalFormat());
//DateTickUnit horizontalTicks = new DateTickUnit();

cyanTimeSeries = new BasicTimeSeries("Random Data", Millisecond.class);
greenTimeSeries = new BasicTimeSeries("Random Data", Millisecond.class);

timeDataset = new TimeSeriesCollection();
timeDataset.addSeries(cyanTimeSeries);
timeDataset.addSeries(greenTimeSeries);

gridStroke = new BasicStroke(0.50f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL);
seriesStroke = new BasicStroke(2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL);

domain = new HorizontalDateAxis();
range = new VerticalNumberAxis();

xyplot = new XYPlot(timeDataset, domain, range);
xyplot.setBackgroundPaint(Color.black);
xyplot.setSeriesPaint(new Paint[] {Color.cyan, Color.green});
xyplot.setSeriesStroke(0, seriesStroke);

domain.setAutoRange(true); //if true, display is blank
domain.setFixedAutoRange(30000.0); //30 second dispaly
domain.setGridPaint(Color.green);
domain.setGridStroke(gridStroke);
domain.setTickLabelsVisible(false);
domain.setGridLinesVisible(false); //vertical grid lines off
domain.setCrosshairVisible(false);

range.setStandardTickUnits(TickUnits.createIntegerTickUnits());
range.setTickUnit(verticalTicks);
range.setRange(0.0, 200.0);
range.setGridPaint(Color.green);
range.setGridStroke(gridStroke);
range.setTickLabelsVisible(false);
range.setGridLinesVisible(true); //horizontal grind lines on
range.setCrosshairVisible(false);

//get the chart and add it to the ChartPnl
chart = new JFreeChart(xyplot);
chartPnl = new ChartPanel(chart);
chartPnl.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLoweredBevelBorder(),
BorderFactory.createEmptyBorder(0,-5,-12,-8) ));

cyanLabel = new JLabel();
cyanLabel.setText("0");
cyanLabel.setFont(BUTTON_FONT);
cyanLabel.setForeground(Color.cyan);
greenLabel = new JLabel("0");
greenLabel.setFont(BUTTON_FONT);
greenLabel.setForeground(Color.green);

cyanLabel.setBackground(Color.black);
cyanLabel.setOpaque(true);
greenLabel.setBackground(Color.black);
greenLabel.setOpaque(true);

add(chartPnl, JLayeredPane.DEFAULT_LAYER);
//add(gridPnl, new Integer(50));
add(cyanLabel, JLayeredPane.PALETTE_LAYER);
add(greenLabel, JLayeredPane.PALETTE_LAYER);

chartPnl.setBounds(7,5,250,130);
cyanLabel.setBounds(13,17,43,14);
greenLabel.setBounds(13,34,43,14);

}//constructor


/** Accessor Methods to update the Green and Cyan plots ************
* and App Notes relating to how to duplicate the CPU usage problem
*
* The following 2 Accessor methods the update the value of the
* green and cyan plots and labels on the OScope Demo.
* setCyanPlot(double y) and setGreenPlot(double y)
*
* Scenario 1:
* First, run this application as is and monitor the CPU usage on
* the Windows Task Manager.In a short period of time, about 10 minutes,
* you will see the CPU usage creep up to 100% --- and stay there!!!
*
* Scenario 2:
* Now, comment the 2 statements that update the
* timeSeries and leave the statements that update the labels
* uncommented and run this application and watch the CPU usage
* you find that the CPU usage is at or near 0% and will never climb
* above it.
*
* The statements to comment are:
* cyanTimeSeries.add(new Millisecond(), y); and
* greenTimeSeries.add(new Millisecond(), y);
*
* Commenting these statements here, keeps the DataGenerator running and
* Scope labels updating and only shuts down the
* JFreeChart TimeSeries classes.
*
* You can change the update rate in the DataGenerator to a slower
* update rate and this run-away CPU usage problem will still occur.
* My design spec is to have a maximum update interval of 100mS.
*
* (I am using a 1GHz P4 processor w/512MByte of RAM)
*
* So, is there another way to do this without encountering the
* excessive CPU usage problem?
*
*/
void setCyanPlot(double y){
cyanTimeSeries.add(new Millisecond(), y); //comment this line
cyanLabel.setText(df.format(y));

}
void setGreenPlot(double y) {
greenTimeSeries.add(new Millisecond(), y); //comment this line
greenLabel.setText(df.format(y));
}


private static int hangCount;
boolean state;

/**
* An inner class to generate random data for the Cyan plot
* and a square wave for the Green plot.
*
* The accessor methods to update the 2 plots are called from
* within the DataGenerator.
*/
class DataGenerator extends Timer implements ActionListener {
private double lastValue;
private double factor;

DataGenerator(){
super(250,null); //DataGenerator update interval
lastValue = 100.0;
this.addActionListener(this);
}
public void actionPerformed(ActionEvent ae){
factor = 0.9 + 0.2*Math.random();
lastValue = lastValue*factor;
//reset the amplitude of the plot
//if it gets too low or too high
if (lastValue<5) {lastValue = lastValue+100;}
if (lastValue>155) {lastValue = lastValue-100;}
setCyanPlot(lastValue);
//setGreenPlot(lastValue-25);

//Square Wave Generator
if (state){
setGreenPlot(110);
} else {
setGreenPlot(10);
}
hangCount++;
if (hangCount == 10) {
state = !state;
hangCount = 0;
}
}//actionPerformed
}//class DataGenerator

public static void main(String[] args) {
JFrame frame = new JFrame("OScope Demo");
OScope scope = new OScope();
frame.getContentPane().add(scope, BorderLayout.CENTER);
frame.setBounds(200,20,300,200);
frame.setVisible(true);
scope.new DataGenerator().start();

frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}//main

}//class

David Gilbert

Re: Excessive CPU Usage

Post by David Gilbert » Tue Sep 10, 2002 10:24 am

Hi Tony,

No definitive answer yet. I ran your demo program for more than an hour without any problems, although the chart refresh slows down as the time series get large (no data is being discarded).

To help with the latter problem, I've modified the BasicTimeSeries class so that you can now set a 'windowCount' attribute...this is the number of TimePeriods in the 'data window', older data is discarded if the windowCount is > 0.

I've modified your test code to create a new com.jrefinery.chart.demo.MemoryUsage demo application, which creates series showing the total and free memory, with a windowCount of 30000 (the most recent 30 seconds of data). I've left this running for some time, it doesn't slow down (since old data is now discarded) but over time it appears that some memory isn't being freed up by the garbage collector. Tracking that down could be tricky.

You can get this code from CVS on SourceForge if you want to try it out.

Regards,

DG.

Irv Thomae

Re: Excessive CPU Usage

Post by Irv Thomae » Tue Sep 10, 2002 4:37 pm

If "some memory isn't being freed up by the garbage collector", that could indeed be tricky to track down (believe me, I know - have been battling a similar problem all summer, in a non-jFreeChart Java application.) Nevertheless, for an application that's intended to run indefinitely, it's essential to solve it.
For the application described, however, what real benefits do the BasicTimeSeries and Millisecond classes really have as compared to simple XYData ? At any rate, it might be a very helpful experiment to rewrite and test this code using plain XYData; that might tell you very quickly whether the performance and memory-leakage problems are side effects of using the time-related classes, or of something else.

David Gilbert

Re: Excessive CPU Usage

Post by David Gilbert » Tue Sep 10, 2002 5:32 pm

Hi Irv,

That's a useful suggestion, I'll give it a try. I'm also going to run the MemoryUsage demo continuously for a while, just to see what happens over a longer period of time. And I want to try some different JVMs and JDKs to see if that gives any clues.

Regards,

DG.

David Gilbert

Re: Excessive CPU Usage

Post by David Gilbert » Wed Sep 11, 2002 12:34 pm

I've been running the MemoryUsage demo (in CVS only) for almost 20 hours now, with no sign of memory leakage (Linux/JDK 1.4)...the garbage collector appears to be doing a good job. So that is promising...

Regards,

DG.

Locked