Memory Problems

May 18, 2011 at 9:15 AM
Edited May 18, 2011 at 9:16 AM

Hi!

I have discovered something that might be a memory leak. If I have a List of objects and let them rotate through the property grid the memory goes up and does not seem to go down. I’ve written a example (if I push the button the objects will rotate through the property grid 100 times each and by looking in the taskmanager I can see the memory going up): 

<UserControl x:Class="SilverlightApplication86.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:slg="clr-namespace:SL40PropertyGrid;assembly=SL40PropertyGrid"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400"
    KeyDown="UserControl_KeyDown">

    <Grid x:Name="LayoutRoot" Background="White">
        <StackPanel Width="400">
            <slg:PropertyGrid SelectedObject="{Binding TestObject}"  x:Name="propertyGrid" DefaultLabelWidth="200"/>
            <Button Width="100" Height="40" Click="Button_Click" />
        </StackPanel>
    </Grid>
</UserControl>

namespace SilverlightApplication86
{
    public partial class MainPage : UserControlINotifyPropertyChanged
    {
        public MainPage()
        {
            DataContext = this;
            for (int i = 0; i < 20; i++)
            {
                testList.Add(new Test());
            }
            TestObject = testList[0];

            InitializeComponent();
        }
        public List<Test> testList = new List<Test>();
        private Test testObject;

        public Test TestObject
        {
            get { return testObject; }
            set { testObject = value; OnPropertyChanged("TestObject"); }
        }
        
        int testObjectCounter;
        private void UserControl_KeyDown(object sender, KeyEventArgs e)
        {
            testObjectCounter++;
            TestObject = testList[testObjectCounter % 20];
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            for (int i = 0; i < 2000; i++)
            {
                UserControl_KeyDown(nullnull);                
            }
        }

        #region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string propertyName)
        {
            if (string.IsNullOrEmpty(propertyName)) throw new ArgumentNullException("propertyName");
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(thisnew PropertyChangedEventArgs(propertyName));
        }
        #endregion

    }
}
    public class Test
    {
        static int counter;
        int myCounter;

        public Test()
        {
            counter++;
            myCounter = counter;
        }

        public int Prop1 { get { return myCounter; } }
        public bool Prop2 { get { return false; } }
    }

In my real program the classes are much larger and I easily get up to 1 GB by rotating through my objects. In that case WinDbg tells me:
Count      Mem   Name
399        46284 SL40PropertyGrid.EnumValueEditor
1115       71360 MS.Internal.TextBoxView
3771       120672 System.EventHandler`1[[SL40PropertyGrid.ExceptionEventArgs, SL40PropertyGrid]]
3771       150840 SL40PropertyGrid.PropertyItem
1995       231420 SL40PropertyGrid.BooleanValueEditor 
8143       260576 System.ComponentModel.PropertyChangedEventHandler
3771       331848 SL40PropertyGrid.PropertyGridLabel

There are less of all of these when I start the program. 
Could it be that the propertygrid create references to its elemets from my objects when the eventhandlers are added and since my objects are not destroyed after being replaced 
in the propertygrid (since the List still refences them) the propertygrid elements can't be collected by the GC.
Everything seems to work if I create a new object to send to the propertygrid every time I change. This is not a problem since it just means that I pass the model like this:
myPropertyGridObject = new TestObject(myObject.Model);
but I would still like to know if there really is a memory leak and if you have any other ideas how to solve it.

Thanx
/Mike 
Coordinator
May 18, 2011 at 3:02 PM

For confirming the memory leak, the simple test, would be run your test, letting the memory build up, then navigate to another page (user control), where the entire usercontrol hosting the PropertyGrid is removed from the visual tree and clean up your object (ie, your TestList<T>), and do something that doesn't create a bunch of objects but is somewhat busy for a period of time (say 1 or 2 minutes). Then come back to the property grid and put just one object into it and see what happens? I don't think it's a memory leak since when you create a new test object each time, its fine.

I think the reason is because it creates a new PropertyItem for each property everytime you set the SelectedObject. So even though it is your same object, there will be a new PropertyItem created everytime. So part of the reason the memory seems high relative to the small number of objects (20 from your example above), is merely because you keep adding the same objects over and over which inturn keeps creating new PropertyItems. If you used a more realistic example of the number of objects that you'd actually be adding, the memory would seem more appropriate compared the number of objects being evaluated. Also you typically would not be using the same objects over and over again. And what is ultimately keeping your objects alive is your TestList<T>.