OutOfMemoryException CustomLabels

TeeChart for Microsoft Visual Studio .NET, Xamarin Studio (Android, iOS & Forms) & Monodevelop.
johnk
Newbie
Newbie
Posts: 31
Joined: Thu Jun 23, 2005 4:00 am

OutOfMemoryException CustomLabels

Post by johnk » Mon May 22, 2006 4:39 pm

Our application continuosly updates the graph and axis. We are encountering a memory leak that appears to be in AxisLabels. It seems the Axis is keeping an array of label positions that are computed in the draw routine, but we cannot find any code that ever removes any entries from the Array. Here is a call stack from an out of memory exception we've encountered:

System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.

at System.Collections.ArrayList.set_Capacity(Int32 value)

at System.Collections.ArrayList.EnsureCapacity(Int32 min)

at System.Collections.ArrayList.Add(Object value)

at Steema.TeeChart.Axis.DrawAxisLabel(ChartFont f, Int32 x, Int32 y, Int32 angle, String st, TextShape format, Boolean isTitle)

at Steema.TeeChart.Axis.DrawAxisLabel(Int32 x, Int32 y, Int32 angle, String st, TextShape labelItem)

at Steema.TeeChart.Axis.AxisDraw.DrawThisLabel(Int32 labelPos, String tmpSt, TextShape labelItem)

at Steema.TeeChart.Axis.AxisDraw.DrawCustomLabels()

at Steema.TeeChart.Axis.AxisDraw.Draw(Boolean calcPosAxis)

at Steema.TeeChart.Axis.Draw(Boolean calcPosAxis)

at Steema.TeeChart.Axes.Draw(Graphics3D g)

at Steema.TeeChart.Chart.InternalDraw(Graphics g)

at Steema.TeeChart.TChart.Draw(Graphics g)

at Steema.TeeChart.TChart.OnPaint(PaintEventArgs pe)

at System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer, Boolean disposeEventArgs)

at System.Windows.Forms.Control.WmPaint(Message& m)

at System.Windows.Forms.Control.WndProc(Message& m)

at AudioPrecision.Common.Controls.HorizontalBar.WndProc(Message& m)

at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)

at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)

at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

Narcís
Site Admin
Site Admin
Posts: 14730
Joined: Mon Jun 09, 2003 4:00 am
Location: Banyoles, Catalonia
Contact:

Post by Narcís » Tue May 23, 2006 8:36 am

Hi johnk,

Could you please send us an example we can run "as-is" to reproduce the problem here?

You can post your files at news://www.steema.net/steema.public.attachments newsgroup.
Best Regards,
Narcís Calvet / Development & Support
Steema Software
Avinguda Montilivi 33, 17003 Girona, Catalonia
Tel: 34 972 218 797
http://www.steema.com
Image Image Image Image Image Image
Instructions - How to post in this forum

johnk
Newbie
Newbie
Posts: 31
Joined: Thu Jun 23, 2005 4:00 am

Post by johnk » Tue May 23, 2006 5:37 pm

It really doesn't take much to reproduce.
Put this into a project and look at it with a memory profiler.

Code: Select all

private void Form1_Load(object sender, EventArgs e)
		{
			FastLine lines = new FastLine(tChart1.Chart);
			lines.FillSampleValues(100);

			timer1.Start();
		}

		private void timer1_Tick(object sender, EventArgs e)
		{
			tChart1.Axes.Bottom.Labels.Items.Clear();
			tChart1.Axes.Bottom.Labels.Items.Add(1.0);
		}
We have the source and I it occurs in Axis.cs DrawAxisLabel.
The shapebounds of the label are being added to an arraylist which is never cleared. The memory profiler shows the number of allocated rectangles keeps rising which would eventually lead to an OutOfMemoryException. We are a week away from code freeze and are trying to figure out a solution.

Narcís
Site Admin
Site Admin
Posts: 14730
Joined: Mon Jun 09, 2003 4:00 am
Location: Banyoles, Catalonia
Contact:

Post by Narcís » Wed May 24, 2006 11:19 am

Hi johnk,

Thanks for the information. We could reproduce the issue here. The problem is the Steema.TeeChart.AxisLabels.labelPos field, which is an arraylist and which isn't being cleared. You can modify the Steema.TeeChart.AxisLabelsItems.Clear method. It should look like:

Code: Select all

/// <summary>
/// Clear Custom Labels list
/// </summary>
#if VS2005
public new void Clear()
#else
public override void Clear()
#endif
{
#if VS2005
foreach (AxisLabelItem c in this) c.Dispose();
#else
for (int t=0; t<base.Count; t++) ((AxisLabelItem)base[t]).Dispose();
#endif
iAxis.Labels.labelPos.Clear(); //CDI new line
base.Clear();
iAxis.Chart.Invalidate();
}
}
This fix has already been applied to current TeeChart sources and will be included with next debug build and maintenance releases.
Best Regards,
Narcís Calvet / Development & Support
Steema Software
Avinguda Montilivi 33, 17003 Girona, Catalonia
Tel: 34 972 218 797
http://www.steema.com
Image Image Image Image Image Image
Instructions - How to post in this forum

johnk
Newbie
Newbie
Posts: 31
Joined: Thu Jun 23, 2005 4:00 am

Post by johnk » Wed May 24, 2006 11:47 pm

I tried building with your changes but now I am getting an overflow in GDI when I try to draw a horizontal bar with an origin set to int.MinValue.
Building without the changes leads to the same exception.

We examined the release binary with Ildasm and found that our version of the source has different code in CalcXPosValue than in the released binary. The version in the Assembly.cs file is 2.0.2179.21171 and the released binary version we compared with is 2.0.2179.21172.

All of our testing has been done with the binary version and we are 2 days from codefreeze. Can we get a patched version of 2.0.2179.21172 with your fix?

Narcís
Site Admin
Site Admin
Posts: 14730
Joined: Mon Jun 09, 2003 4:00 am
Location: Banyoles, Catalonia
Contact:

Post by Narcís » Thu May 25, 2006 11:41 am

Hi johnk,

We have done several tests here. We have compiled the sources of the build you mentioned and compared its assemblies with the binary installers using reflection and CalcXPosValue was the same.

Also, we tried to reproduce the issue you reported without success. The code we used is:

Code: Select all

    private void Form1_Load(object sender, EventArgs e)
    {
      horizBar1.FillSampleValues();
      horizBar1.UseOrigin = true;
      horizBar1.Origin = int.MinValue;
    }
We strongly recommend to use the latest binary and source code releases available at our Customer Download Area and apply the change we suggested in the sources.
Best Regards,
Narcís Calvet / Development & Support
Steema Software
Avinguda Montilivi 33, 17003 Girona, Catalonia
Tel: 34 972 218 797
http://www.steema.com
Image Image Image Image Image Image
Instructions - How to post in this forum

johnk
Newbie
Newbie
Posts: 31
Joined: Thu Jun 23, 2005 4:00 am

Post by johnk » Fri May 26, 2006 5:09 pm

I downloaded the sources and still have the Overflow when setting the origin. Again if I examine CalcXPosValue in the released binary that I downloaded it is truncating the value then calling Utils.Round. The source that I downloaded does not call Utils.Round in the CalcXPosValue function.

Narcís
Site Admin
Site Admin
Posts: 14730
Joined: Mon Jun 09, 2003 4:00 am
Location: Banyoles, Catalonia
Contact:

Post by Narcís » Mon May 29, 2006 8:35 am

Hi johnk,

The Utils.Round call is added by Reflector. We are unable to reproduce the issue here. Does the error happen with the assembly I sent you on Friday?
Best Regards,
Narcís Calvet / Development & Support
Steema Software
Avinguda Montilivi 33, 17003 Girona, Catalonia
Tel: 34 972 218 797
http://www.steema.com
Image Image Image Image Image Image
Instructions - How to post in this forum

johnk
Newbie
Newbie
Posts: 31
Joined: Thu Jun 23, 2005 4:00 am

Post by johnk » Tue May 30, 2006 6:43 pm

No the error does not occur with the one you sent on Friday. I'm having difficulty explaining how reflector could add the call to Steem.TeeChart.Utils.Round.

Taking Relector out of the equation:
When I examine the latest assemblies with Ildasm it shows a call to Utils.Round but only in the released binary from Steema. The assembly that I build with the latest source does not show the call.

Narcís
Site Admin
Site Admin
Posts: 14730
Joined: Mon Jun 09, 2003 4:00 am
Location: Banyoles, Catalonia
Contact:

Post by Narcís » Wed May 31, 2006 8:26 am

Hi johnk,

Lookin more carefully to Reflector's output and our internal sources, the difference between them and you source version is because our TeeChart sources are like this:

Code: Select all

		/// <summary>
		/// Calculates the Horizontal coordinate in pixels of Value parameter.
		/// </summary>
		/// <param name="value">Parameter Value </param>
		/// <returns>Horizontal coordinate in pixels </returns>
		public int CalcXPosValue(double value) 
		{
			//AQTime
			if (IsDepthAxis) 
			{
				if(chart.Aspect.View3D)
					return InternalCalcDepthPosValue(value); 
				else 
					return 0;
			}
			else 
				if (logarithmic)
				return InternalCalcLogPosValue(true,value);
			else 
				if (iRangezero) return iCenterPos;
			else 
			{

				// compile with #define CHECKOVER 
				// if you wish to avoid axis coordinates overflow when zooming,
				// specially in Windows 95, 98 or Me.
#if CHECKOVER

				double tmp=(value-iMinimum)*iAxisSizeRange;
				tmp = inverted ? IEndPos-tmp : IStartPos+tmp;
				if (tmp>MaxPixelPos) tmp=MaxPixelPos;
				else
					if (tmp<-MaxPixelPos) tmp=-MaxPixelPos;
				return Utils.Round(tmp);

#else  // faster version (no checking)...

				int tmp=(int)((value-iMinimum)*iAxisSizeRange);
				return inverted ? IEndPos-tmp : IStartPos+tmp; 

#endif
			}
		}
Source code release should have the CHECKOVER section but for some reason it is not included. We will check it for next releases.
Best Regards,
Narcís Calvet / Development & Support
Steema Software
Avinguda Montilivi 33, 17003 Girona, Catalonia
Tel: 34 972 218 797
http://www.steema.com
Image Image Image Image Image Image
Instructions - How to post in this forum

johnk
Newbie
Newbie
Posts: 31
Joined: Thu Jun 23, 2005 4:00 am

Post by johnk » Wed May 31, 2006 3:22 pm

Thank you for clearing that up. I look forward to the next release.

Narcís
Site Admin
Site Admin
Posts: 14730
Joined: Mon Jun 09, 2003 4:00 am
Location: Banyoles, Catalonia
Contact:

Post by Narcís » Thu Jun 01, 2006 8:48 am

Hi johnk,

You're welcome. Please be aware at this forum for debug builds and maintenance releases announcements.
Best Regards,
Narcís Calvet / Development & Support
Steema Software
Avinguda Montilivi 33, 17003 Girona, Catalonia
Tel: 34 972 218 797
http://www.steema.com
Image Image Image Image Image Image
Instructions - How to post in this forum

johnk
Newbie
Newbie
Posts: 31
Joined: Thu Jun 23, 2005 4:00 am

Post by johnk » Fri Jun 16, 2006 3:29 pm

Clearing the arraylist in the Clear method will work only if we call clear before every draw. The arraylist seems to add the label positions in the draw method even if the labels have not changed.
Since we do not always clear the labels before drawing, the arraylist still grows until we get an OutOfMemoryException.

johnk
Newbie
Newbie
Posts: 31
Joined: Thu Jun 23, 2005 4:00 am

Post by johnk » Fri Jun 16, 2006 4:35 pm

It looks like the arraylist is only needed for handling the click label event. Since we do not use this feature we have implemented this workaround.

After initializing the chart we get the labelpos arraylist using reflection and subscribe to the GetAxisDrawLabel event.

Code: Select all

FieldInfo labelPosInfo = 
    chartAxis.Labels.GetType().GetField("labelPos", BindingFlags.NonPublic | BindingFlags.Instance);

if (labelPosInfo != null)
    this.labelPos = labelPosInfo.GetValue(chartAxis.Labels) as ArrayList;

if (this.labelPos != null)
{
    this.axis.GetAxisDrawLabel += new GetAxisDrawLabelEventHandler(chartAxis_GetAxisDrawLabel);
}
Then is getaxisdrawlabel we clear the arraylist.

Code: Select all

void chartAxis_GetAxisDrawLabel(object sender, GetAxisDrawLabelEventArgs e)
{
    //clear the list of leaking rectangles
    if (this.labelPos != null)
        this.labelPos.Clear();
}

Narcís
Site Admin
Site Admin
Posts: 14730
Joined: Mon Jun 09, 2003 4:00 am
Location: Banyoles, Catalonia
Contact:

Post by Narcís » Mon Jun 19, 2006 8:09 am

Hi johnk,

If I remember correctly you already reported this issue some days ago and we fixed it in the sources and sent you the solution. The problem was at Axis.cs in Steema.TeeChart.Axis.AxisDraw, InternalDrawLabel method. It ended up being like below, we had to add the labelPos.Clear line.

Code: Select all

			private void InternalDrawLabel(bool decValue) 
			{
				int tmp=axis.CalcPosValue(tmpValue);
				if ((axis.labels.bOnAxis) || 
					((tmp>axis.IStartPos) &&
(tmp<axis.IEndPos))) 
				{
					if (!axis.TickOnLabelsOnly)
AddTick(tmp);
          if (axis.labels.Visible)
          {
            axis.labels.labelPos.Clear();
            DrawThisLabel(tmp, axis.labels.LabelValue(tmpValue), null);
          }
				}
				if (decValue) axis.IncDecDateTime(false,ref tmpValue,iIncrement,tmpWhichDatetime);
			}
BTW: This issue is already fixed in the latest debug build available at the customer area.
Best Regards,
Narcís Calvet / Development & Support
Steema Software
Avinguda Montilivi 33, 17003 Girona, Catalonia
Tel: 34 972 218 797
http://www.steema.com
Image Image Image Image Image Image
Instructions - How to post in this forum

Post Reply