Improve company productivity with a Business Account.Sign Up

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1556
  • Last Modified:

Silverlight 4 Memory Leak

I've been battling a memory leak in my SL4 app for days now. Running ANTS memory profiler indicates an unmanaged memory leak, but I can't see where this is happening.

The app is a dashboard style app that runs 24/7 and queires a WCF Service every 20s to get new data. I'm calling all closeasyc(), and nulling the client reference each time, but the memory still continues to grow.

Th interesting thing is that if I minimize the app, the memory stops growing. I found this very odd.

On timer tick i'm now only running the code below. The Bitmapimage and Image declarations at in the constructor. I did try creating on each pass, then nulling the bitmapimage and source, but this didn't help.

Does anyone know where to look for unmanaged memory leaks, as I'm out of ideas.


public void DailyElecDataSeries()
        {
            if (!Chart1Built) { CreateChart1(); }
           // Chart1Grid.Children.Clear();
            if (Chart1.Series.Count == 0)
            {
                Chart1.Series.Add(new DataSeries() { RenderAs = RenderAs.Column });
                Chart1.Series.Add(new DataSeries() { RenderAs = RenderAs.Column });
            }
            
            sDate = DateTime.Now.ToShortDateString();
            sDate2 = DateTime.Now.AddDays(-1).ToShortDateString();
            CO2Multiplier = ObjRefs[CurrentBuilding].ElecCO2;
            CarbonMultiplier = ObjRefs[CurrentBuilding].ElecCarbon;


            try
            {
                decimal maxSample=0;
                decimal consumption = 0;
                foreach (var c in ElecDayData)
                {
                    decimal dec;
                    if (Decimal.TryParse(c._day_data.ToString(), out dec))
                    {
                        consumption += dec;
                        if (dec > maxSample) maxSample = dec;


                    }
                }

                Value1 = Decimal.Round(consumption, 0).ToString() + " kWh";
                Value2 = (Decimal.Round(consumption * Convert.ToDecimal(CO2Multiplier), 0).ToString()) + " kg";
                //Value3 = (Decimal.Round((Convert.ToInt32((ElecDayData.Compute("sum(day_data)*" + CarbonMultiplier + "", "")))), 2).ToString()) + " kg";
                TodayCO2 = Convert.ToDouble(Decimal.Round(consumption * Convert.ToDecimal(CO2Multiplier), 2).ToString());

                decimal prevconsumption = 0;
                foreach (var c in ElecPrevDayData)
                {
                    decimal dec;
                    if (Decimal.TryParse(c._day_data.ToString(), out dec))
                    {
                        prevconsumption += dec;
                        if (dec > maxSample) maxSample = dec;

                    }
                }

                Value3 = Decimal.Round(prevconsumption, 0).ToString() + " kWh";
                Value4 = (Decimal.Round(prevconsumption * Convert.ToDecimal(CO2Multiplier), 0).ToString()) + " kg";
                //Value6 = (Decimal.Round((Convert.ToInt32((ElecPrevDayData.Compute("sum(day_data)*" + CarbonMultiplier + "", "")))), 2).ToString()) + " kg";

                Chart1.AxesY[0].AxisMaximum = (maxSample + 1).ToString();
                //Also add in the Axis Title at this point,X Axis Title is added in another Method
                Chart1.AxesY[0].Title = "kWh";
                
                // Set DataSeries property

                Chart1.Series[0].LegendText = "On Hours (kWh)";
                Chart1.Series[1].LegendText = "Off Hours (kWh)";


                if (count == 0)
                {
                    Chart1.Titles[0].Text = "Electricity Usage Today";
                    if (!Chart1Built)
                    {
                        foreach (var c in ElecDayData)
                        {
                            dataPoint = new DataPoint();
                            dataPoint.AxisXLabel = Convert.ToDateTime(c._Date).ToString("HH:mm");

                            // Update YValue property of the DataPoint
                            dataPoint.YValue = 0;

                            //  Add dataPoint to DataPoints collection
                            Chart1.Series[0].DataPoints.Add(dataPoint);

                            dataPoint = null;

                            dataPoint = new DataPoint();
                            dataPoint.AxisXLabel = Convert.ToDateTime(c._Date).ToString("HH:mm");

                            // Update YValue property of the DataPoint
                            dataPoint.YValue = 0;

                            //  Add dataPoint to DataPoints collection
                            Chart1.Series[1].DataPoints.Add(dataPoint);
                            dataPoint = null;

                        }
                        //Added in to allow animation of datapoints following series clearing
                        for (Int32 i = 0; i < ElecDayData.Count; i++)
                        {
                            Chart1.Series[0].DataPoints[i].YValue = ElecDayData[i]._OnHoursTotal;
                            Chart1.Series[1].DataPoints[i].YValue = ElecDayData[i]._OffHoursTotal;

                        }
                    }
                    else
                    {
                        for (Int32 i = 0; i < ElecDayData.Count; i++)
                        {
                            Chart1.Series[0].DataPoints[i].YValue = ElecDayData[i]._OnHoursTotal;
                            Chart1.Series[1].DataPoints[i].YValue = ElecDayData[i]._OffHoursTotal;

                        }
                    }
                }
                else
                {
                    Chart1.Titles[0].Text = "Electricity Usage Yesterday";
                    if (!Chart1Built)
                    {
                        foreach (var c in ElecPrevDayData)
                        {
                            dataPoint = new DataPoint();
                            dataPoint.AxisXLabel = Convert.ToDateTime(c._Date).ToString("HH:mm");

                            // Update YValue property of the DataPoint
                            dataPoint.YValue = 0;

                            //  Add dataPoint to DataPoints collection
                            Chart1.Series[0].DataPoints.Add(dataPoint);

                            dataPoint = null;

                            dataPoint = new DataPoint();
                            dataPoint.AxisXLabel = Convert.ToDateTime(c._Date).ToString("HH:mm");

                            // Update YValue property of the DataPoint
                            dataPoint.YValue = 0;

                            //  Add dataPoint to DataPoints collection
                            Chart1.Series[1].DataPoints.Add(dataPoint);
                            dataPoint = null;

                        }
                        //Added in to allow animation of datapoints following series clearing
                        for (Int32 i = 0; i < ElecPrevDayData.Count; i++)
                        {
                            Chart1.Series[0].DataPoints[i].YValue = ElecPrevDayData[i]._OnHoursTotal;
                            Chart1.Series[1].DataPoints[i].YValue = ElecPrevDayData[i]._OffHoursTotal;

                        }
                    }
                    else
                    {
                        for (Int32 i = 0; i < ElecPrevDayData.Count; i++)
                        {
                            Chart1.Series[0].DataPoints[i].YValue = ElecPrevDayData[i]._OnHoursTotal;
                            Chart1.Series[1].DataPoints[i].YValue = ElecPrevDayData[i]._OffHoursTotal;

                        }
                    }

                }
                


            }


            catch (Exception ex)
            {
                string msg = "Daily Electricity Chart Series Rendering Error: ";
                msg += ex.InnerException.ToString();
                throw new Exception(msg);

            }
            finally
            {
                if (!Chart1Built) { Chart1Grid.Children.Add(Chart1); }
                Chart1Built = true;
               
                //Chart1 = null;
                //Chart1Day=null;
                //Chart1PrevDay = null;
                //Chart1AxisY = null;




            }
        }


AND




public void ODBCData(int Count)
        {
            tBox1Grid.Children.Clear();
            tBox2Grid.Children.Clear();
            tBox3Grid.Children.Clear();
            tBox4Grid.Children.Clear();

            

            try
            {

                if (Count == 1)
                {

                    ElecPower = Convert.ToDouble(ODBCDatalist[CurrentBuilding].ElecPower);
                    ElecEquiv = Math.Round(((TodayCO2 / kgPerGallon) * MPG) / 100, 0);
                    //myGauge1.CurrentValue = Math.Round(ElecPower, 0);
                    txtStatus.Text = "Today, this Building has emitted the same CO2 as " + ElecEquiv.ToString() + " family Cars covering 100miles";
                    StatuxBox.Header = "Current Status - Electricity";
                    txtCost.Text = (Math.Round(ElecPower * Convert.ToDouble(ElecCostPerkWh), 2)).ToString() + " £/hr";
                    txtCO2.Text = (Math.Round(ElecPower * ElecCO2, 0)).ToString() + " kgCO2/h";
                    txtElecPower.Text = Math.Round(ElecPower, 1).ToString() + " kW";
                    transImg1.Source = img1;
                    transImg2.Source = img3;
                    transImg4.Source = img5;

                }
                if ((Count == 3) && (GasAvailable == 1))
                {
                    GasPower = Convert.ToDouble(ODBCDatalist[CurrentBuilding].GasPower);
                    //GasGauge.CurrentValue = Math.Round(GasPower, 0);
                    GasCO2 = Math.Round((YesterdayGasUsage * GasCO2) * HomeCO2perYear, 4);
                    txtStatus.Text = "Yesterday, this Building emitted the equivalent CO2 of  " + YesterdayGasCO2.ToString() + " Households Annual Emissions";
                    StatuxBox.Header = "Current Status - Gas";
                    txtCost.Text = (Math.Round(GasPower * Convert.ToDouble(GasCostPerkWh), 2)).ToString() + " £/h";
                    txtCO2.Text = (Math.Round(GasPower * GasCO2, 0)).ToString() + " kgCO2/h";
                    txtElecPower.Text = Math.Round(GasPower, 1).ToString() + " kW";
                    transImg1.Source = img2;
                    transImg2.Source = img4;
                    transImg4.Source = img5;

                }
                if (Count == 5)
                {
                    ElecPower = Convert.ToDouble(ODBCDatalist[CurrentBuilding].ElecPower);
                    ElecEquiv = Math.Round(((ElecPower * 1000) / LaptopPower), 0);
                    //myGauge1.CurrentValue = Math.Round(ElecPower, 0);
                    txtStatus.Text = "Today this Building has used the Equivalent Power of " + ElecEquiv.ToString() + " 40-Watt Laptop(s) per hour";
                    StatuxBox.Header = "Current Status - Electricity";
                    txtCost.Text = (Math.Round((ElecPower * Convert.ToDouble(ElecCostPerkWh)) * 12, 2)).ToString() + " £/Day";
                    txtCO2.Text = (Math.Round((ElecPower * ElecCO2) * kgCoal, 0)).ToString() + " kg Coal/h";
                    txtElecPower.Text = Math.Round(ElecPower, 1).ToString() + " kW";
                    transImg1.Source = img7;
                    transImg2.Source = img3;
                    transImg4.Source = img6;

                }
                if (Count == 7)
                {
                    ElecPower = Convert.ToDouble(ODBCDatalist[CurrentBuilding].ElecPower);
                    ElecEquiv = Math.Round(((ElecPower * 1000) / LaptopPower), 0);
                    //myGauge1.CurrentValue = Math.Round(ElecPower, 0);
                    txtStatus.Text = "Today this Building has used the Equivalent Power of " + ElecEquiv.ToString() + " 40-Watt Laptop(s) per hour";
                    StatuxBox.Header = "Current Status - Electricity";
                    txtCost.Text = (Math.Round((ElecPower * Convert.ToDouble(ElecCostPerkWh)) * 12, 2)).ToString() + " £/Day";
                    txtCO2.Text = (Math.Round((ElecPower * ElecCO2) * kgCoal, 0)).ToString() + " kg Coal/hr";
                    txtElecPower.Text = Math.Round(ElecPower, 1).ToString() + " kW";
                    transImg1.Source = img7;
                    transImg2.Source = img3;
                    transImg4.Source = img6;

                }
                if ((Count == 9) && (WaterAvailable == 1))
                {
                    WaterEquiv = Math.Round(((TodayWater * 1000) / BathUsage), 0);
                    txtStatus.Text = "Today this Building has used enough water to fill " + WaterEquiv.ToString() + " Baths";
                    StatuxBox.Header = "Current Status - Water";
                    txtCost.Text = (Math.Round((WaterCurrentRate * WaterCostm3), 2)).ToString() + " £/hr";
                    txtCO2.Text = (Math.Round((WaterCurrentRate * 1000) / BathUsage, 0)).ToString() + " Baths/hr";
                    txtElecPower.Text = Math.Round(WaterCurrentRate, 1).ToString() + " m3/h";
                    transImg1.Source = img10;
                    transImg2.Source = img9;
                    transImg4.Source = img11;

                }
                transImg3.Source = img8;


            }
            catch (Exception ex)
            {
                string msg = "ODBC Data Collection Error: ";
                msg += ex.InnerException.ToString(); ;
                throw new Exception(msg);
            }
            finally
            {

                tBox1Grid.Children.Add(transImg1);
                //System.Threading.Thread.Sleep(1000);
                tBox2Grid.Children.Add(transImg2);
                //System.Threading.Thread.Sleep(1000);
                tBox3Grid.Children.Add(transImg3);
                //System.Threading.Thread.Sleep(1000);
                tBox4Grid.Children.Add(transImg4);
                

            }


        }

Open in new window

0
wint100
Asked:
wint100
  • 11
  • 7
2 Solutions
 
Carl TawnSystems and Integration DeveloperCommented:
Well the first place to look would be any unmanaged resources you aer using (i.e. your images). At what point, and how, are you disposing of them?

Also any database connections/external connections could be another area. Is it possible for you to disable all of your image stuff so you can purely test the interaction with the web service?
0
 
wint100Author Commented:
Ok, originally I was using the following in the finally block:

img1=null;
transimg1=null;

But this is when I was creating the 'img1' and transimage1 in the method on each tick. I've recently changed this so that the BitmapImage(img1) is created in the 'Loaded' event of the page.

I've tried disbaling all image declerations, and am testing now. It's been running 3hours now and has gone up slightly, but not as much. Although the image method also calls a WCF Service to get the ODBCDatalist data.

As I'm testing in VS2010 debugging, could this be injecting any memory leak, or could the ASP.NET side of things be accounting for any memory leak. I'm just thinking if it's the service itself not the silverlight client.


img1 = new BitmapImage(src1);
            img2 = new BitmapImage(src2);
            img3 = new BitmapImage(src3);
            img4 = new BitmapImage(src4);
            img5 = new BitmapImage(src5);
            img6 = new BitmapImage(src6);
            img7 = new BitmapImage(src7);
            img8 = new BitmapImage(src8);
            img9 = new BitmapImage(src9);
            img10 = new BitmapImage(src10);
            img11 = new BitmapImage(src11);

Open in new window

0
 
Carl TawnSystems and Integration DeveloperCommented:
Simply setting the reference to null doesn't delete the image from memory, it simply makes the reference point to nothing.

You need to explicitly call teh Dispose() method on the image to make sure it's resources are released correctly.
0
Get expert help—faster!

Need expert help—fast? Use the Help Bell for personalized assistance getting answers to your important questions.

 
wint100Author Commented:
I couldn't see a dispose() method on image, that's why I'm nulling them out. Although now I'm not creating the images each pass, there should only be a single set in memory anyway, right?

It's a long grim process, that's been causing me grief for days..
0
 
Carl TawnSystems and Integration DeveloperCommented:
Are you able to comment out either all of the images, or at least most of them, in order to try and verify if it is the images that are causing the problem?
0
 
wint100Author Commented:
Currently the app is running in debug, on seems to be reclaiming memory, but the ODBCData method that uses the images is not being called. Only the DailyElecDataSeries() method above is running.

If the images are declared at constructor level, shouldn't they only be in memory once?
0
 
Carl TawnSystems and Integration DeveloperCommented:
What do you mean by "declared at contructor level"? If you mean you create the image in the constructor, then there will be a set of images for each instance of the class.
0
 
wint100Author Commented:
That's what I mean. Created in the constructor of the page. So if the single page is running 24/7, shouldn't there be a single set of images for the page, and no more created until the page is restarted?
0
 
Carl TawnSystems and Integration DeveloperCommented:
Where is the code that loads the images? Is it in the ASPX page that hosts your Silverlight app, or is it in the SL app itself?
0
 
wint100Author Commented:
Everything is in sl page code. Other than wcf.
0
 
Carl TawnSystems and Integration DeveloperCommented:
Are your images changing at any point? Only you are currently resetting the ImageSource on each pass, but if the images are static then this probably isn't necessary.
0
 
wint100Author Commented:
Yes the spurce changes depending on the count that is incremented on timer tick.

Would it help if i sent you the full page code as txt file?
0
 
Carl TawnSystems and Integration DeveloperCommented:
It would be useful to see the code in context yes. Just attach to the question.
0
 
wint100Author Commented:
Will do when I get home(1-2hrs)
0
 
wint100Author Commented:
Code attached. Feel free to criticise ANY part of this code, as I am a newb, and currently trying to teach myself the .NET  language. ANY recommendation on the code in any way is welcomed.
PageCode.txt
0
 
wint100Author Commented:
WRONG CODE ATTACHED, CORRECT CODE ATTACHED HERE..
PageCode.txt
0
 
wint100Author Commented:
Does seem odd that the memory leak is NOT present when the OOB application is minimized..
0
 
wint100Author Commented:
Ok, seems there is an issue with silverlight that was causing a memory leak in Visifire charts. Visifire have modded their code to avoid ths silverlight issue, and the memory leak is pretty much clear. A big improvement anyway.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Get your problem seen by more experts

Be seen. Boost your question’s priority for more expert views and faster solutions

  • 11
  • 7
Tackle projects and never again get stuck behind a technical roadblock.
Join Now