Solved

Silverlight 4 Memory Leak

Posted on 2010-09-05
18
1,444 Views
Last Modified: 2013-11-12
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
Comment
Question by:wint100
  • 11
  • 7
18 Comments
 
LVL 52

Assisted Solution

by:Carl Tawn
Carl Tawn earned 500 total points
Comment Utility
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
 
LVL 1

Author Comment

by:wint100
Comment Utility
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
 
LVL 52

Expert Comment

by:Carl Tawn
Comment Utility
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
 
LVL 1

Author Comment

by:wint100
Comment Utility
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
 
LVL 52

Expert Comment

by:Carl Tawn
Comment Utility
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
 
LVL 1

Author Comment

by:wint100
Comment Utility
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
 
LVL 52

Expert Comment

by:Carl Tawn
Comment Utility
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
 
LVL 1

Author Comment

by:wint100
Comment Utility
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
 
LVL 52

Expert Comment

by:Carl Tawn
Comment Utility
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
How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

 
LVL 1

Author Comment

by:wint100
Comment Utility
Everything is in sl page code. Other than wcf.
0
 
LVL 52

Expert Comment

by:Carl Tawn
Comment Utility
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
 
LVL 1

Author Comment

by:wint100
Comment Utility
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
 
LVL 52

Expert Comment

by:Carl Tawn
Comment Utility
It would be useful to see the code in context yes. Just attach to the question.
0
 
LVL 1

Author Comment

by:wint100
Comment Utility
Will do when I get home(1-2hrs)
0
 
LVL 1

Author Comment

by:wint100
Comment Utility
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
 
LVL 1

Author Comment

by:wint100
Comment Utility
WRONG CODE ATTACHED, CORRECT CODE ATTACHED HERE..
PageCode.txt
0
 
LVL 1

Author Comment

by:wint100
Comment Utility
Does seem odd that the memory leak is NOT present when the OOB application is minimized..
0
 
LVL 1

Accepted Solution

by:
wint100 earned 0 total points
Comment Utility
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

Featured Post

Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

Join & Write a Comment

This article is for Object-Oriented Programming (OOP) beginners. An Interface contains declarations of events, indexers, methods and/or properties. Any class which implements the Interface should provide the concrete implementation for each Inter…
It was really hard time for me to get the understanding of Delegates in C#. I went through many websites and articles but I found them very clumsy. After going through those sites, I noted down the points in a easy way so here I am sharing that unde…
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
This tutorial demonstrates a quick way of adding group price to multiple Magento products.

728 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now