This is a migrated thread and some comments may be shown as answers.

Prism + Telerick = Memory Leak

11 Answers 338 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
Victor
Top achievements
Rank 1
Victor asked on 01 May 2012, 05:47 AM
Hello,

I am using WPF 4.0 and Prism 4.0 with MEF and the latest Radcontrols for WPF. I have 15 core modules containing mostly RadGridViews and RadCharts. These 15 modules are implemented as prism views in a single Window. All modules stay loaded as the application is expected to be long running. Three of the views toggle to a secondary view because I ran out of real estate on the screen. 

The application is a read only dashboard so I am using MVVM without any command bindings. Viewmodels contain an ObservableCollection of the data class and bind to the Itemsource of the control. All Telerick controls are updating as the data source updates and seem to be operating fine.

However, I am experiencing a heavy memory leak that is mostly in Telerick code.

My profiler lists these classes as the highest offenders:
Telerick.Windows.Data.QueryableCollectionView
Telerick.Windows.Data.CompositeFilterDescriptorCollection
Telerick.Windows.Data.SelectDescriptorCollection
Telerick.Windows.Data.CompositeFilterDescriptor

Before each update I call Clear() on the ObservableCollection in the Viewmodel but this doesn't seem to help. I derive the Viewmodel from INotifyPropertychanged but it doesn't help and is not needed for update since the ObservableCollection takes care of it. If I raise the property changed event the leak only increases.
 
I have traced references in my profiler back to the shell and the prism region which live for the lifetime of the application.

Has anyone experienced this behavior? Are Telerick controls known to leak memory in Prism applications? This must be common scenario for Telerick controls althought I have found little on the web in regards to a Prism memory leak so thought I would post here.

Thanks in advance for any help.
 Victor

11 Answers, 1 is accepted

Sort by
0
Victor
Top achievements
Rank 1
answered on 01 May 2012, 06:00 AM
Forgot to add to above post:

I also see a major memory leak in System.Reflection.Emit with a class called __FixupData[]

I have no idea what this class is emitting and hope it is not jitting the Telerick controls when they update. Tracing references in my profiler leads back to a regular expression. System.Text.RegularExpressions.Regex and a root of Function.s_typeParser

Any clues?

0
Rossen Hristov
Telerik team
answered on 01 May 2012, 09:13 AM
Hi,

Someone must be holding onto these objects and not releasing them. Unfortunately, without a runnable sample project which we can run and profile we will not be able to tell who is holding those objects.

If you can send us a stripped-down dummy version of your project which we can successfully run and profile we might be able to tell what is going on.

Meanwhile, I am not sure which profiler you are using, but the ANTS one is pretty awesome and can tell much more than WinDbg, i.e. gcroot-ing the offending objects.

As for the FixupData[] thing, I don't think it is one of our classes.

Kind regards,
Ross
the Telerik team

Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

0
Victor
Top achievements
Rank 1
answered on 01 May 2012, 10:55 PM
Thanks for your reply Ross.

I take it there are no known issues with Telerik controls and Prism?

I am using the ANTS memory profilier. Most objects trace back to the shell (main window) of the application or MEF or a resource. Again, no controls or prism views are released or destroyed. This is a WPF dashboard application meant to be long running. After running for 18 hours I have a 350 MB memory leak. The __FixupData[ ] object seems to be the largest offender and seems to be generating IL for the Telerik.Windows.Data.QueryableCollectionView object in a RadChart in my core module 10. 

Core 10 is a RadChart with horizontal bars. It displays 10-12 bars at a time when it updates. It's Itemsource is bound to an ObservableCollection of a class that holds it's data objects. On every update cycle I Clear() the collection and then populate with a new set of objects. The RadChart updates itself with these objects. Is it somehow holding on to the previous set of objects? Is there a way to clear it?

I have attached a PNG of the retention graph from ANTS in hope you can decipher it. This would be the end result of running a sample app, right? I will try to create a sample app but it will be difficult to not include everything in the app. Perhaps you can find a smoking gun in the attachment.

Thanks again for your attention to this matter.

Victor
0
Yavor
Telerik team
answered on 04 May 2012, 09:31 AM
Hi Victor,

Thank you for sharing the retention graph!

As this image shares some light on what is going on, without a runnable sample app that we can debug in our labs, we can only guess what is going on.

We have developed a completely new charting solution that doesn't use QueryableCollectionView under the hood and is designed with memory and performance in mind. You can try replacing RadChart with RadChartView and profile your application again. I am sure you will be pleased with the performance improvement. You can find information on RadChartView in our online help system here. If you experience a problem in the transition process I will be happy to assist you!

Regards,
Yavor
the Telerik team

Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

0
Efren
Top achievements
Rank 1
answered on 11 May 2012, 04:55 AM
Thanks for your response.

I am unable to refactor my code for the RadChartView at this time since there is no migration path for it. Perhaps in a later version I'll have time in my schedule to do that.

I did however come up with a solution to stop the memory leak with RadChart and MVVM. I will share this so others in the community can benefit from it.

In a nutshell, when updating RadChart dynamically from a database or any real time data source, you cannot simply clear your ObservableCollection and refill with new values since RadChart binds to them and never lets go. This was the primary source of the memory leak. Every update cycle I would Clear() the collection in my ViewModel and reload with a new set of data from the Model. In my scenario database tables are cleared each update cycle and all rows are deleted before updating with new data.

The solution is to reuse items in the ObservableCollection and let the property changed mechanism work. When clearing items in the collection and reloading with new objects, RadChart must re-bind to each new object. This must be the default behavior of the QueryableCollection in RadChart. 

Here's a code snippet that illustrates it:

_dc.ocReserve is a List<EntityType> from the EntityFramework. This gets updated in a background thread along with the ViewModel which contains the ObservableCollection n1 that is bound to the RadChart. The trick is to reuse an existing item in the collection instead of clearing it and let the INotifyPropertyChanged mechanism update RadChart from the Model. (Core4Model in this case)

if (_dc.ocReserve.Count > 0)
{
    //reuse existing slots in n1
    // n1 is the ObservableCollection
    foreach (ReservationEntity x in _dc.ocReserve)
    {
        if (n1.Count > 0 && y < n1.Count)
        {
            n1[y].PartySize = x.PartySize;
            n1[y++].ReservationTime = x.ReservationTime;
        }           
         // or create a new one
        else if (_dc.ocReserve.Count > n1.Count)
        {
            Core4Model m = new Core4Model();
            m.PartySize = x.PartySize;
            m.ReservationTime = x.ReservationTime;
            n1.Add(m);
            y++;
        }
    }
 
    // if we have less items in the source than the collection let's remove the old ones
    if (_dc.ocReserve.Count < n1.Count)
    {
        for (int i = n1.Count - _dc.ocReserve.Count; i != 0; i--)
        {
            n1.RemoveAt((y - 1) + i);
        }
    }
}
else
     //in the event there is nothing in the database go ahead and clear it 
    n1.Clear();

This simple reuse pattern has eliminated the leak. Luckily in my case database rows tend to grow and not shrink because any shrinkage will still cause a small memory leak.

Again, this is a workaround for using RadChart with dynamic updates and MVVM. BTW, the latest Telerik Visual Studio add-in I used dropped the RadChart on my page right out of the gate. Perhaps you should consider switching to the RadChartView since this is what you recommend moving forward.

Victor G.  
0
Yavor
Telerik team
answered on 15 May 2012, 01:58 PM
Hi Victor,

Thank you for your detailed inside! I will forward it to our development team for consideration.

All the best,
Yavor
the Telerik team

Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

0
Mark
Top achievements
Rank 1
answered on 17 Jan 2013, 08:46 PM

Regarding repopulating the ObservableCollection: I have a question about the DataLoaded event and knowing then the collection is actually finished loading.

Case 1) When repopulating the list, with each item individually, the DataLoaded event gets called multiple times.

Case 2) When using:
  myViewer.ItemsSource = myObservableCollection
the DataLoaded event only gets called when all items are loaded.

So, if I am repopulating the list (the solution recommended here), is there a way to know when the last item is loaded?

Thank you
Mark

0
Ves
Telerik team
answered on 22 Jan 2013, 03:27 PM
Hi Mark,

I am not sure I followed you completely. Is RadGridView involved here?

As a general advice -- you can use Telerik.Windows.Data.RadObservableCollection -- it exposes API to suspend and resume change notifications, so you get only a single CollectionChanged event. Here is an example:

Telerik.Windows.Data.RadObservableCollection<int> myCollection = new Telerik.Windows.Data.RadObservableCollection<int>();
 myCollection.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(a_CollectionChanged);
 myCollection.SuspendNotifications();
 myCollection.Add(5);
 myCollection.Add(5);
 myCollection.ResumeNotifications();
.......
 
void a_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            // This is executed just once.
        }


Best regards,
Ves
the Telerik team

Explore the entire Telerik portfolio by downloading Telerik DevCraft Ultimate.

0
Mark
Top achievements
Rank 1
answered on 22 Jan 2013, 05:54 PM
Hmmm.. .I did not now about RadObservableCollections.

Yes RadGridView is involved. I want to assign new items to the ItemsSource.

This, and other, posts suggest (to reduce memory leaks) that it is not a good idea to simply assigning a new collection, instead they suggest to reuse the same collection.
ie. 
1) // Memory Leak way
  myGridView.ItemsSource = collectionData;   // previously assigned collection of data is not cleaned up properly

2) // Better way (reuse the collection)
// setup
  myDataCollection = new ObservableCollection();
  myGridView.ItemsSource = myDataCollection;

// later, when new data is available
myDataCollection.Clear()
myDataCollection.Add(item)
etc.

However, the advantage of the case 1) was that the GridView.DataLoaded is only called once. That way, I can know when all the data is finished loading. Method 2) above will cause the DataLoaded method to be called after every item is added.

If I use the RadObservableCollection and Suspend notifications while I am loading (and resume them when it is finished), will that cause the GridView.DataLoaded method to only be called once (when it is finished)?

Thank you for your help and patience with my explanations :-)
0
Ves
Telerik team
answered on 25 Jan 2013, 09:45 AM
Hi Mark,

That's correct. Using the approach shown in the code snippet in my previous message DataLoaded event will be triggered just once. In fact -- this will happen within ResumeNotifications method.

Best regards,
Ves
the Telerik team

Explore the entire Telerik portfolio by downloading Telerik DevCraft Ultimate.

0
Mark
Top achievements
Rank 1
answered on 29 Jan 2013, 10:40 PM
Great, thank you for your help.
Tags
General Discussions
Asked by
Victor
Top achievements
Rank 1
Answers by
Victor
Top achievements
Rank 1
Rossen Hristov
Telerik team
Yavor
Telerik team
Efren
Top achievements
Rank 1
Mark
Top achievements
Rank 1
Ves
Telerik team
Share this question
or