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