Is this memory leak?

4 posts, 1 answers
  1. King Chan
    King Chan avatar
    30 posts
    Member since:
    Feb 2010

    Posted 13 Sep 2010 Link to this post

    Dear Telerik Staff,

    I wonder if I did the garbage collection incorrectly, or this is memory leak?
    I spent few days to research about Silverlight's memory leak issues and I know it affected Telerik's controls as well, I just want to see if this is caused by same reason or it is just the way I do the garbage collection was wrong.


    The demo I created is very simple, Button "Create Obj" will create 10 RadScheduler and add into LayoutRoot and to a List<RadScheduler>.

    Then the Button "Collects" will run though the List<RadSchdeduler> to remove each RadScheduler from LayoutRoot, then set each RadScheduler object to null, Clear() the list, then do the: 

    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect(); 


    1) Before Click Create Obj Button:
    Task Manager:
    IExplorer.exe
    Memory(Private Working Set): 42,828k
    Performance Memory: 656MB

    2) After Click Create Obj Button:
    Task Manager:
    IExplorer.exe
    Memory(Private Working Set): 194,828k
    Performance Memory: 805MB

    3) After Click "Collect" Button:
    Task Manager:
    IExplorer.exe
    Memory(Private Working Set): 194,280k
    Performance Memory: 801MB

    For same scenario, if I create Button obj(500 Buttons) instead of RadScheduler, I can see the memory increased few MB and released few MB properly.
    But again, for same scenario, if I create RadDialog(10 RadDialog with no Content) obj this time instead of RadScheduler, then I close it, null it, GC.Collect it, it will release PART OF the memory, and have around 1 MB didn't release.

    And here is the demo code (everything created in MainPage.xaml.cs):

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using Telerik.Windows.Controls;
    using System.Reflection.Emit;
    using System.Diagnostics;
      
    namespace RadControlsSilverlightApp4
    {
        public partial class MainPage : UserControl
        {
            TextBlock aLabel = new TextBlock();
      
            List<RadScheduler> SchedulerList = new List<RadScheduler>();
            public MainPage()
            {
                InitializeComponent();
                this.LayoutRoot.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(100, GridUnitType.Auto) });
                this.LayoutRoot.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(100, GridUnitType.Pixel) });
                this.LayoutRoot.RowDefinitions.Add(new RowDefinition { Height = new GridLength(100, GridUnitType.Auto) });
                this.LayoutRoot.RowDefinitions.Add(new RowDefinition { Height = new GridLength(70, GridUnitType.Auto) });
                this.LayoutRoot.RowDefinitions.Add(new RowDefinition { Height = new GridLength(70, GridUnitType.Auto) });
                this.LayoutRoot.RowDefinitions.Add(new RowDefinition { Height = new GridLength(500, GridUnitType.Auto) });
      
                Button aButton = new Button();
                aButton.Content = "Create Objs";
                aButton.SetValue(Grid.RowProperty, 0);
                aButton.SetValue(Grid.ColumnProperty, 0);
                this.LayoutRoot.Children.Add(aButton);
        
                Button aCollectButton = new Button();
                aCollectButton.Content = "Collects";
                aCollectButton.SetValue(Grid.RowProperty, 1);
                aCollectButton.SetValue(Grid.ColumnProperty, 0);
                this.LayoutRoot.Children.Add(aCollectButton);
      
                aLabel.SetValue(Grid.RowProperty, 1);
                aLabel.SetValue(Grid.ColumnProperty, 1);
                aLabel.Text = GC.GetTotalMemory(true).ToString();
                this.LayoutRoot.Children.Add(aLabel);
      
                Button aUpdateButton = new Button();
                aUpdateButton.Content = "Update";
                aUpdateButton.SetValue(Grid.RowProperty, 2);
                aUpdateButton.SetValue(Grid.ColumnProperty, 0);
                this.LayoutRoot.Children.Add(aUpdateButton);
      
                aButton.Click +=new RoutedEventHandler(aButton_Click);
                aCollectButton.Click +=new RoutedEventHandler(aCollectButton_Click);
                aUpdateButton.Click += new RoutedEventHandler(aUpdateButton_Click);
      
            }
      
            public void aButton_Click (object sender, RoutedEventArgs e){
                Debug.WriteLine("Create 10 Obj");
                   
                for (int i = 0; i < 10; i++)
                  
                    RadScheduler aScheduler = new RadScheduler();
                    aScheduler.SetValue(Grid.RowProperty, 3);
                    aScheduler.SetValue(Grid.ColumnProperty, 1);
                    SchedulerList.Add(aScheduler);
                    this.LayoutRoot.Children.Add(aScheduler);
                     
                }
      
                aLabel.Text = GC.GetTotalMemory(false).ToString(); 
                Debug.WriteLine("Obj Count: " + SchedulerList.Count.ToString());
                Debug.WriteLine("Obj Count: " + this.LayoutRoot.Children.Count.ToString());
            }
      
            public void aCollectButton_Click(object sender, RoutedEventArgs e)
            
      
                for (int i = 0; i < SchedulerList.Count; i++)
                {
                    this.LayoutRoot.Children.Remove(SchedulerList[i]);
                    SchedulerList[i].DataContext = null;                
                    SchedulerList[i] = null;
                }
                SchedulerList.Clear();
      
                Debug.WriteLine("Collect!");
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                Debug.WriteLine("Collected!");
                Debug.WriteLine("Obj Count: " + SchedulerList.Count.ToString());
                Debug.WriteLine("Obj Count: " + this.LayoutRoot.Children.Count.ToString());
                aLabel.Text = GC.GetTotalMemory(false).ToString();
            }
      
      
            public void aUpdateButton_Click(object sender, RoutedEventArgs e)
            {
                Debug.WriteLine("Update!");
                Debug.WriteLine("Obj Count: " + this.LayoutRoot.Children.Count.ToString());
                aLabel.Text = GC.GetTotalMemory(false).ToString();
      
            }
        }
    }
    =========
    Update: I just used WeakReference to check, I realize none of the RadScheduler get collected, they are all Alive.
    And for the RadDialog, I can see few of the RadDialog object is Alive, and others was got collected, it happens if I try keep clicking Create and Collect frequently. (i.e Created like 500 RadDialog on screen, after collects, there is 3 alives)
    =========
    Thanks for your help,
    King


  2. Answer
    Hristo
    Admin
    Hristo avatar
    832 posts

    Posted 14 Sep 2010 Link to this post

    Hello King Chan,

    Unfortunately Silverlight 4 GDR1 did not fix all memory leaks. We are in a process of preparing Q2 Service Pack 2 (which workarounds known framework leaks and also fix few memory leaks caused by our code).
    It will be official in a week or two.

    Let us know if you need more information.

    Greetings,
    Hristo
    the Telerik team
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
  3. King Chan
    King Chan avatar
    30 posts
    Member since:
    Feb 2010

    Posted 14 Sep 2010 Link to this post

    Thanks for your reply, I guess that's mean I didn't did wrong on collecting?
    I am still new to silverlight, just want to make sure.
  4. Hristo
    Admin
    Hristo avatar
    832 posts

    Posted 15 Sep 2010 Link to this post

    Hi King Chan,

    When testing for memory leaks I don't observe Task Manager (working set). Garbage collection is not something that can be controlled. The correct approach is to create WeakReferences.

    I hope that this will help you.

    All the best,
    Hristo
    the Telerik team
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
Back to Top