Silverlight Memory Leak while using WCF RIA Services

When we want to load data through RIA services, the code we usaully write looks loke bellow:

_context = new ProductContext();
_context.Load(_context.GetProductsQuery(), d=>
{
      this.Products=new ObservableCollection<Product>(d.Entities);
}, null);             
 
But the above code will cause a great increase of memory usage, enven through you call GC.Collect() after the load operation completes.
 

Solution

The solution for this problem is as simple as the following code shows:
private void LoadProducts()
   {
        _context = new ProductContext();
        _context.Load(_context.GetProductsQuery(), LoadBehavior.MergeIntoCurrent, LoadProductsCompleted, null);
   }
 
    private void LoadProductsCompleted(LoadOperation<Product> loadOperation)
    {
        if (this.Products == null)
        {
            this.Products = new ObservableCollection<Product>(loadOperation.Entities);
        }
    }
As you have noticed that the key point is to use LoadBehavior. There are 3 values of LoadBehavior:
  • LoadBehavior.KeepCurrent: The cached instance is not changed and the newly read instance is discarded. If an instance’s key property (marked as KeyAttribute) doesn’t change, this instance will not be updated. This is a very important feature!
  • LoadBehavior.MergeIntoCurrent: Values from the newly read instance are merged into the cached instance for property values that are unmodified. No changes are lost in this merge.
  • LoadBehavior.RefreshCurrent: All members of the cached instance are overwritten with current values from the newly read instance, regardless of whether they have been modified. In addition, the original state of the entity is also set to the newly read instance.
 
You can use the right load behavior to extremly improve your app’s performance.
 

Research report

About the former problem and solution, I have done a test to show how great the solution can save memory usage.
In the test, the ViewModel loads 2500 products every 5 seconds, and each products’ IDs do not change (range from 1 to 2500). First, let’s see how the memory usage increases when using the old context.Load() method:
 
The following picture shows the memory usages when the new context.Load() method is applied.
 
 

Conclusion

  1.  All the entities loaded to the client side are cached in Silverlight.
  2.  Everytime when you new an instance, the instance will be cached, and it will make memory usage increase. I don’t know if we can clear the cache or dispose the instance manually, but till now, I haven’t found the way.
  3.  If you use frames in your Silverlight applications, please set the pages’ NavigationCacheMode property to "Enabled", since all the pages are actually cached, even if you set the property to “Disabled”.
 
 This section will help you better understand the memory leak in Silverlight 4.