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