This is a migrated thread and some comments may be shown as answers.

RadChart Memory Leak

3 Answers 121 Views
Chart (Obsolete)
This is a migrated thread and some comments may be shown as answers.
John
Top achievements
Rank 1
John asked on 26 Oct 2012, 07:36 PM
I am having a similar problem, and I generated a short example to illustrate the problem. We are using a timer, but it happens to be a bit longer in cycle than the one supplied here. It just takes longer to run out of memory if the timer is longer. Note that the more items that are added to the chart, the larger the memory leak.

I also took the liberty of looking through the Telerik source code and discovered that RadChart contains 2 Chart objects, one directly, and one indirectly through MapAreaBuilder. RadChart does not implement Dispose, so these items hang around much longer than they should. MapAreaBuilder also does not implement dispose, and it should as well. The basic idea is that if a class includes an object that implements IDisposable, the containing class must also implement IDisposable and dispose of the Disposable objects it contains. Yes, RadChart does inherit from Control that implements IDispose, but Control does not know to dispose of the 2 Charts that are contained in the RadChart object. In other words, RadChart needs to override Dispose(bool) and dispose of the Chart and the MapAreaBuilder if the parameter passed in is true. MapAreaBuilder needs to derive from IDisposable, implement Dispose(), Dispose(bool) and ~MapAreaBuilder().

Default.aspx.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Telerik.Web.UI;
namespace RadChartLeakTest
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Timer1Tick(object sender, EventArgs e)
{
}
}
}

Default.aspx
<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
CodeBehind="Default.aspx.cs" Inherits="RadChartLeakTest._Default" %>
<%@ Register TagPrefix="telerik" Namespace="Telerik.Web.UI" Assembly="Telerik.Web.UI" %>
<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
</asp:Content>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
<telerik:RadScriptManager ID="RadScriptManager1" runat="server">
</telerik:RadScriptManager>
<telerik:RadScriptBlock ID="Script1" runat="server">
<script type="text/javascript">
</script>
</telerik:RadScriptBlock>
<asp:Panel ID="PanelUpdateThis" runat="server">
<telerik:RadChart runat="server" ID="Chart1" >
<Appearance Border-Visible="false" FillStyle-FillType="Solid" FillStyle-MainColor="#f2f2f2">
</Appearance>
<ChartTitle Visible="false">
</ChartTitle>
<Legend Appearance-Border-Visible="false" Appearance-FillStyle-FillType="Solid" Appearance-FillStyle-MainColor="#f2f2f2">
</Legend>
<PlotArea Appearance-Border-Color="#f2f2f2" Appearance-FillStyle-MainColor="#f2f2f2"
Appearance-FillStyle-FillType="Solid" EmptySeriesMessage-TextBlock-Text="No Sessions Last 24 Hours"
EmptySeriesMessage-TextBlock-Appearance-TextProperties-Color="Black">
<XAxis AutoScale="false" IsZeroBased="true">
</XAxis>
</PlotArea>
<Series>
</Series>
</telerik:RadChart>
<asp:Timer ID="Timer1" runat="server" Interval="1000" OnTick="Timer1Tick">
</asp:Timer>
</asp:Panel>
</asp:Content>


3 Answers, 1 is accepted

Sort by
0
Yavor
Telerik team
answered on 31 Oct 2012, 12:19 PM
Hi John,

Thank you for the in-depth report. I sent it yo our developers and they managed to track down and fix the leaks you reported. They will be included in the next major release.

Greetings,
Yavor
the Telerik team
If you want to get updates on new releases, tips and tricks and sneak peeks at our product labs directly from the developers working on the RadControls for ASP.NET AJAX, subscribe to their blog feed now.
0
John
Top achievements
Rank 1
answered on 07 Oct 2014, 01:57 PM
Was this ever fixed and released?  I still have the workaround code in place, and we would like to remove it.
0
Danail Vasilev
Telerik team
answered on 08 Oct 2014, 11:32 AM
Hello John,

In the Telerik UI release notes (i.e., Q3 2012 SP1 and Q3 2012 SP2) posted after the date of my colleague's answer (i.e., 31-Oct-2012) there doesn't seem to figure such an item out.

This, however, is an omission in the release notes because the RadChart's code consists of "Fix: Memory leak in RadChart" change set checked-in on 11/01/2012 and it looks like this:
RadChart.cs:
...
        public override void Dispose()
        {
            if (this._chart != null)
            {
                this._chart.Dispose();
            }
            if (this._mapAreaBuilder != null)
            {
                this._mapAreaBuilder = null;
            }
            base.Dispose();
        }
...

Chart.cs:
internal void CalculateChart()
{
    int width = (int)this.Appearance.Dimensions.Width.PixelValue;
    int height = (int)this.Appearance.Dimensions.Height.PixelValue;
    if (width > 0 && height > 0)
    {
        using (RenderEngine renderEngine = new RenderEngine(this, width, height))
        {
            Legend.BindSeriesToLegend(renderEngine);
        }
    }
}
 
/// <summary>
/// Chart recalculation
/// </summary>
internal void ReCalculateChart()
{
    int width = (int)this.Appearance.Dimensions.Width.PixelValue;
    int height = (int)this.Appearance.Dimensions.Height.PixelValue;
    if (width > 0 && height > 0)
    {
        using (RenderEngine renderEngine = new RenderEngine(this, width, height))
        {
            renderEngine.InitializeChartElements();
            renderEngine.CalculateElementsForRender();
        }
    }
}


internal Image GetStaticArea(int width, int height, bool withXAxis, bool withYAxis, bool withYAxis2)
{
    this.Skin = this.Skin;
    if (width > 0 && height > 0)
    {
        RenderEngine renderEngine = new RenderEngine(this, width, height);
        try
        {
            CheckLimitations();
            renderEngine.InitializeChartElements();
            OnBeforeLayout(this, EventArgs.Empty);
            chartPlotArea.PrepareForScale();
            renderEngine.CalculateElementsForRender();
            OnPrePaint(this, null);
            return renderEngine.RenderChartArea(true, true, true, true, false, withXAxis, withYAxis, withYAxis2);
        }
        catch (ChartException ex)
        {
            return GetException(renderEngine, ex);
        }
        catch (Exception ex)
        {
            return GetException(renderEngine, new ChartException(DefaultValues.EXCEPTION_MESSAGE, ex));
        }
        finally
        {
            renderEngine.Dispose();
            renderEngine = null;
        }
    }
 
    return null;
}

internal Image GetPlotArea(int width, int height, float xScale, float yScale, Unit clientWidth, Unit clientHeight, Unit top, Unit left)
{
    this.Skin = this.Skin;
 
    if (width > 0 && height > 0)
    {
        // Chart elements calculation after the post back
        ReCalculateChart();
 
        float scaledImageWidth = GetScaledImageWidth(xScale, yScale);
        float scaledImageHeight = GetScaledImageHeight(xScale, yScale);
 
        RenderEngine renderEngine = new RenderEngine(this, (int)Math.Round(scaledImageWidth), (int)Math.Round(scaledImageHeight), false);
        renderEngine.InitGraphics(width, height); // init ChartGraphics object with unscaled image size
        try
        {
            ChartPlotArea plotArea = chartPlotArea;
            PrepareForScale(xScale, yScale);
            renderEngine.InitializeChartElements();
            renderEngine.ScalePlotArea(xScale, yScale);
 
            float plotLeft = (float)Math.Round(plotArea.Appearance.Position.X);
            float plotTop = (float)Math.Round(plotArea.Appearance.Position.Y);
            int plotWidth = (int)Math.Round(clientWidth.PixelValue);
            int plotHeight = (int)Math.Round(clientHeight.PixelValue);
 
            if (SeriesOrientation == ChartSeriesOrientation.Vertical)
            {
                plotLeft += plotArea.YAxis.Appearance.Width;
            }
            else
            {
                plotLeft += plotArea.XAxis.Appearance.Width;
 
                if (plotArea.YAxis2.IsVisible())
                    plotTop += plotArea.YAxis2.Appearance.Width;
            }
 
            renderEngine.InitGraphics(plotWidth, plotHeight);
            renderEngine.graphics.TranslateTransformDefault(-plotLeft - left.PixelValue, -plotTop - top.PixelValue);
 
            Image img = renderEngine.RenderPlotArea(true);
 
            RestoreAfterScale(width, height);
 
            return img;
        }
        catch (ChartException ex)
        {
            return GetException(renderEngine, ex);
        }
        catch (Exception ex)
        {
            return GetException(renderEngine, new ChartException(DefaultValues.EXCEPTION_MESSAGE, ex));
        }
        finally
        {
            renderEngine.Dispose();
            renderEngine = null;
        }
    }
 
    return null;
}

internal Image GetAxis(int width, int height, float xScale, float yScale, ChartAxisType axisType)
{
    this.Skin = this.Skin;
 
    if (width > 0 && height > 0)
    {
        float w = width;
        float h = height;
 
        if (axisType == ChartAxisType.XAxis)
            yScale = 1;
        else
            xScale = 1;
 
        bool isChartVertical = this.SeriesOrientation == ChartSeriesOrientation.Vertical;
 
        if ((axisType == ChartAxisType.XAxis && isChartVertical) || (axisType != ChartAxisType.XAxis && !isChartVertical))
        {
            w = GetScaledImageWidth(xScale, yScale);
        }
        else
        {
            h = GetScaledImageHeight(xScale, yScale);
        }
 
        if (w > 0 && h > 0)
        {
            RenderEngine renderEngine = new RenderEngine(this, w, h, true);
            ChartPlotArea plotArea = chartPlotArea;
 
            try
            {
                PrepareForScale(xScale, yScale);
                renderEngine.InitializeChartElements();
                renderEngine.ScalePlotArea(xScale, yScale);
 
                RectangleF axisRect;
                ChartAxis axis = null;
 
                switch (axisType)
                {
                    case ChartAxisType.XAxis:
                        axis = plotArea.XAxis;
                        break;
                    case ChartAxisType.YAxis:
                        axis = plotArea.YAxis;
                        break;
                    case ChartAxisType.YAxis2:
                        axis = plotArea.YAxis2;
                        break;
                    default:
                        break;
                }
 
                Image img = null;
                if (axis != null)
                {
                    axisRect = axis.GetClientRectangle();
 
                    if (axisRect.Width > 0 && axisRect.Height > 0)
                    {
                        renderEngine.InitGraphics((int)Math.Round(axisRect.Width), (int)Math.Round(axisRect.Height));
                        renderEngine.graphics.TranslateTransformDefault(-(int)Math.Round(axisRect.X), -(int)Math.Round(axisRect.Y));
                        img = renderEngine.RenderAxis(true, axisType);
                    }
                }
 
                RestoreAfterScale(width, height);
 
                return img;
            }
            catch (ChartException ex)
            {
                return GetException(renderEngine, ex);
            }
            catch (Exception ex)
            {
                return GetException(renderEngine, new ChartException(DefaultValues.EXCEPTION_MESSAGE, ex));
            }
            finally
            {
                renderEngine.Dispose();
                renderEngine = null;
            }
        }
    }
 
    return null;
}

I can also suggest that you try the newer RadHtmlChart control that renders entirely on the client instead of the obsolete RadChart.


Regards,
Danail Vasilev
Telerik
 

Check out the Telerik Platform - the only platform that combines a rich set of UI tools with powerful cloud services to develop web, hybrid and native mobile apps.

 
Tags
Chart (Obsolete)
Asked by
John
Top achievements
Rank 1
Answers by
Yavor
Telerik team
John
Top achievements
Rank 1
Danail Vasilev
Telerik team
Share this question
or