C#, Visual Basic and C++

Managing Memory in Windows Store Apps, Part 2

Chipalo Street
Dan Taylor

 

In the Windows 8 special edition of MSDN Magazine, the first article in this series discussed how memory leaks occur, why they slow down your app and degrade the overall system experience, general ways to avoid leaks, and specific issues that have been found to be problematic in JavaScript apps (see “Managing Memory in Windows Store Apps,” msdn.microsoft.com/magazine/jj651575). Now we’ll look at memory leaks in the context of C#, Visual Basic and C++ apps. We’ll analyze some basic ways leaks have occurred in past generations of apps, and how Windows 8 technologies help you avoid these situations. With this foundation, we’ll move to more complex scenarios that can cause your app to leak memory. Let’s get to it!

Simple Cycles

In the past, many leaks were caused by reference cycles. Objects involved in the cycle would always have an active reference even if the objects in the cycle could never be reached. The active reference would keep the objects alive forever, and if a program created these cycles frequently, it would continue to leak memory over time.

A reference cycle can occur for multiple reasons. The most obvious is when objects explicitly reference each other. For example, the following code results in the picture in Figure 1:

Foo a = new Foo();
Bar b = new Bar();
a.barVar = b;
b.fooVar = a;

A Circular Reference
Figure 1 A Circular Reference

Thankfully, in garbage-collected languages such as C#, JavaScript and Visual Basic, this kind of circular reference will be automatically cleaned up once the variables are no longer needed.

C++/CX, in contrast, doesn’t use garbage collection. Instead, it relies on reference counts to perform memory management. This means that objects will be reclaimed by the system only when they have zero active references. In these languages, the cycles between these objects would force A and B to live forever because they would never have zero references. Even worse, everything referenced by A and B would live forever as well. This is a simplified example that can be easily avoided when writing basic programs; however, complex programs can create cycles that involve multiple objects chaining together in non-obvious ways. Let’s take a look at some examples.

Cycles with Event Handlers

As discussed in the earlier article, event handlers are an extremely common way for circular references to be created. Figure 2 shows how this might occur.

Figure 2 Causing a Circular Reference with an Event Handler

<MainPage x:Class="App.MainPage" ...>
  ...
  <TextBlock x:Name="displayTextBlock" ... />
  <Button x:Name="myButton" Click="ButtonClick" ... />
  ...
</MainPage>
public sealed partial class MainPage : Page
{
  ...
  private void ButtonClick(object sender, RoutedEventArgs e)
  {
    DateTime currentTime = DateTime.Now;
    this.displayTextBlock.Text = currentTime.ToString();
  }
  ...
}

Here we’ve simply added a Button and TextBlock to a Page. We’ve also set up an event handler, defined on the Page class, for the Button’s Click event. This handler updates the text in the TextBlock to show the current time whenever the Button is clicked. As you’ll see, even this simple example has a circular reference.

The Button and TextBlock are children of the page and therefore the page must have a reference to them, as shown in the top diagram in Figure 3.

A Circular Reference Related to the Event Handler
Figure 3 A Circular Reference Related to the Event Handler

In the bottom diagram, another reference is created by the registration of the event handler, which is defined on the Page class.

The event source (Button) has a strong reference to the event handler, a delegate method, so that the source can call the event handler when the event is fired. Let’s call this delegate a strong delegate because the reference from it is strong.

We now have a circular reference. Once the user navigates away from the page, the garbage collector (GC) is smart enough to reclaim the cycle between the Page and Button. These types of circular references will be automatically cleaned up when they’re no longer needed if you’re writing apps in JavaScript, C# or Visual Basic. As we noted earlier, however, C++/CX is a ref-counted language, which means objects are automatically deleted only when their reference count drops to zero. Here, the strong references created would force the Page and Button to live forever because they would never have zero reference counts. Even worse, all of the items contained by the Page (potentially a very large element tree) would live forever as well because the Page holds references to all of these objects.

Of course, creating event handers is an extremely common scenario and Microsoft doesn’t want this to cause leaks in your app regardless of the language you use. For that reason the XAML compiler makes the reference from the delegate to the event listener a weak reference. You can think of this as a weak delegate because the reference from the delegate is a weak reference.

The weak delegate ensures that the page isn’t kept alive by the reference from the delegate to the page. The weak reference will not count against the page’s reference count and thus will allow it to be destroyed once all other references drop to zero. Subsequently, the Button, TextBlock and anything else referenced by the page will be destroyed as well.

Long-Lived Event Sources

Sometimes an object with a long lifetime defines events. We refer to these events as long-lived because the events share the lifetime of the object that defines them. These long-lived events hold references to all registered handlers. This forces the handlers, and the objects targeted by the handlers, to stay alive as long as the long-lived event source.

In the “Event Handler” section of the previous memory leak article, we analyzed one example of this. Each page in an app registers for the app window’s SizeChangedEvent. The reference from the window’s SizeChangedEvent to the event handler on the page will keep each instance of Page alive as long as the app’s window is around. All of the pages that have been navigated to remain alive even though only one of them is in view. This leak is easily fixed by unregistering each page’s SizeChangedEvent handler when the user navigates away from the page.

In that example, it’s clear when the page is no longer needed and the developer is able to unregister the event handler from the page. Unfortunately it’s not always easy to reason about an object’s lifetime. Consider simulating a “weak delegate” in C# or Visual Basic if you find leaks caused by long-lived events holding on to objects via event handlers. (See “Simulating ‘Weak Delegates’ in the CLR” at bit.ly/SUqw72.) The weak delegate pattern places an intermediate object between the event source and the event handler. Use a strong reference from the event source to the intermediate object and a weak reference from the intermediate object to the event handler, as shown in Figure 4.

Using an Intermediate Object Between the Event Source and the Event Listener
Figure 4 Using an Intermediate Object Between the Event Source and the Event Listener

In the top diagram in Figure 4, LongLivedObject exposes EventA and ShortLivedObject registers EventAHandler to handle the event. LongLivedObject has a much greater life span than ShortLived­Object and the strong reference between EventA and EventAHandler will keep ShortLivedObject alive as long as LongLivedObject. Placing an IntermediateObject between LongLivedObject and ShortLivedObject (as shown in the bottom diagram) allows IntermediateObject to be leaked instead of ShortLivedObject. This is a much smaller leak because the IntermediateObject needs to expose only one function, while ShortLivedObject may contain large data structures or a complex visual tree.

Let’s take a look at how a weak delegate could be implemented in code. An event many classes may want to register for is Display­Properties.OrientationChanged. DisplayProperties is actually a static class, so the OrientationChanged event will be around forever. The event will hold a reference to each object you use to listen to the event. In the example depicted in Figure 5 and Figure 6, the class LargeClass uses the weak delegate pattern to ensure that the OrientationChanged event holds a strong reference only to an intermediate class when an event handler is registered. The intermediate class then calls the method, defined on LargeClass, which actually does the necessary work when the OrientationChanged event is fired.

The Weak Delegate Pattern
Figure 5 The Weak Delegate Pattern

Figure 6 Implementing a Weak Delegate

public class LargeClass
{
  public LargeClass()
  {
    // Create the intermediate object
    WeakDelegateWrapper wrapper = new WeakDelegateWrapper(this);
    // Register the handler on the intermediate with
    // DisplayProperties.OrientationChanged instead of
    // the handler on LargeClass
    Windows.Graphics.Display.DisplayProperties.OrientationChanged +=
      wrapper.WeakOrientationChangedHandler;
  }
  void OrientationChangedHandler(object sender)
  {
    // Do some stuff
  }
  class WeakDelegateWrapper : WeakReference<LargeClass>
  {
    DisplayPropertiesEventHandler wrappedHandler;
    public WeakDelegateWrapper(LargeClass wrappedObject,
      DisplayPropertiesEventHandler handler) : base(wrappedObject)
    {
      wrappedHandler = handler;
      wrappedHandler += WeakOrientationChangedHandler;
    }
    public void WeakOrientationChangedHandler(object sender)
    {
      LargeClass wrappedObject = Target;
      // Call the real event handler on LargeClass if it still exists
      // and has not been garbage collected. Remove the event handler
      // if LargeClass has been garbage collected so that the weak
      // delegate no longer leaks
      if(wrappedObject != null)
        wrappedObject.OrientationChangedHandler(sender);  
      else
        wrappedHandler -= WeakOrientationChangedHandler;
    }
  }
}

Lambdas

Many people find it easier to implement event handlers with a lambda—or inline function—instead of a method. Let’s convert the example from Figure 2 to do exactly that (see Figure 7).

Figure 7 Implementing an Event Handler with a Lambda

<MainPage x:Class="App.MainPage" ...>
  ...
  <TextBlock x:Name="displayTextBlock" ... />
  <Button x:Name="myButton" ... />
  ...
</MainPage>
public sealed partial class MainPage : Page
{
  ...
  protected override void OnNavigatedTo
  {
    myButton.Click += => (source, e)
    {
      DateTime currentTime = DateTime.Now;
      this.displayTextBlock.Text = currentTime.ToString();
    }
  ...
}

Using a lambda also creates a cycle. The first references are still obviously created from the Page to the Button and the TextBlock (like the top diagram in Figure 3).

The next set of references, illustrated in Figure 8, is invisibly created by the lambda. The Button’s Click event is hooked up to a RoutedEventHandler object whose Invoke method is implemented by a closure on an internal object created by the compiler. The closure must contain references to all variables referenced by the lambda. One of these variables is “this,” which—in the context of the lambda—refers to the Page, thus creating the cycle.

References Created by the Lambda
Figure 8 References Created by the Lambda

If the lambda is written in C# or Visual Basic, the CLR GC will reclaim the resources involved in this cycle. However, in C++/CX this kind of reference is a strong reference and will cause a leak. This doesn’t mean that all lambdas in C++/CX leak. A circular reference wouldn’t have been created if we hadn’t referenced “this” and only used variables local to the closure when defining the lambda. As one solution to this problem, if you need to access a variable external to the closure in an inline event handler, implement that event handler as a method instead. This allows the XAML compiler to create a weak reference from the event to the event handler and the memory will be reclaimed. Another option is to use pointer-to-member syntax, which allows you to specify whether a strong or weak reference is taken against the class containing the pointer-to-member method (in this case, the Page).

Use the Event Sender Parameter

As discussed in the previous article, each event handler receives a parameter, typically called “sender,” which represents the event source. The event source parameter of a lambda helps avoid circular references. Let’s modify our example (using C++/CX ) so the button shows the current time when it’s clicked (see Figure 9).

Figure 9 Making the Button Show the Current Time

<MainPage x:Class="App.MainPage" ...>
  ...
  <Button x:Name="myButton" ... />
  ...
</MainPage>
MainPage::MainPage()
{
   ...
   myButton->Click += ref new RoutedEventHandler(
     [this](Platform::Object^ sender, 
     Windows::UI::Xaml::RoutedEventArgs^ e)
   {    
     Calendar^ cal = ref new Calendar();
     cal->SetToNow() ;
     this->myButton->Content = cal->SecondAsString();
   });
   ...
}

The updated lambda creates the same circular references illustrated in Figure 8. They will cause C++/CX to leak, but this can be avoided by using the source parameter instead of referencing myButton through the “this” variable. When the closure method is executed, it creates the “source” and “e” parameters on the stack. These variables live only for the duration of the method call instead of for as long as the lambda is attached to the Button’s event handler (currentTime has the same life span). Here’s the code to use the source parameter:

MainPage::MainPage()
{
  ...
  myButton->Click += ref new RoutedEventHandler([](Platform::Object^ sender,
  Windows::UI::Xaml::RoutedEventArgs^ e)
  {    
    DateTime currentTime ;
    Calendar^ cal = ref new Calendar();
    cal->SetToNow() ;
    Button ^btn = (Button^)sender ;
    btn->Content = cal->SecondAsString();  });
  ...
}

The references now look like what’s shown in Figure 10. The reference depicted in red, creating the cycle, is present only during the execution of the event handler. This reference is destroyed once the event handler has completed and we’re left with no cycles that will cause a leak.

Using the Source Parameter
Figure 10 Using the Source Parameter

Use WRL to Avoid Leaks in Standard C++ Code

You can use standard C++ to create Windows Store apps, in addition to JavaScript, C#, C++/CX and Visual Basic. When doing so, familiar COM techniques are employed, such as reference counting to manage the lifetime of objects and testing HRESULT values to determine whether an operation succeeded or failed. The Windows Runtime C++ Template Library (WRL) simplifies the process of writing this code ( bit.ly/P1rZrd). We recommend you use it when implementing standard C++ Windows Store apps to reduce any bugs and memory leaks, which can be extremely difficult to locate and resolve.

Use Event Handlers That Cross Language Boundaries with Caution

Finally, there’s one coding pattern that requires special attention. We’ve discussed the possibility of leaks from circular references that involve event handlers, and that many of these cases can be detected and avoided by platform-supplied mitigations. These mitigations do not apply when the cycle crosses multiple garbage-collected heaps.

Let’s take a look at how this might happen, as shown in Figure 11.

Figure 11 Displaying a User’s Location

<Page x:Class="App.MainPage" ...>
  ...
  <TextBlock x:Name="displayTextBlock" ... />
  ...
</Page>
public sealed partial class MyPage : Page
{
  ...
  Geolocator gl;
  protected override void OnNavigatedTo{} ()
  {
    Geolocator gl = new Geolocator();
    gl.PositionChanged += UpdatePosition;
  }
  private void UpdatePosition(object sender, RoutedEventArgs e)
  {
    // Change the text of the TextBlock to reflect the current position
  }
  ...
}

This example is very similar to the previous examples. A Page contains a TextBlock that displays a little information. In this sample, though, the TextBlock displays the user’s location, as shown in Figure 12.

Circular References Span a Garbage-Collector Boundary
Figure 12 Circular References Span a Garbage-Collector Boundary

At this point, you probably could’ve drawn the circular references yourself. What’s not obvious, however, is that the circular references span a garbage-collector boundary. Because the references extend outside the CLR, the CLR GC can’t detect the presence of a cycle and this will leak. It’s difficult to prevent these types of leaks because you can’t always tell in which language an object and its events are implemented. If Geolocator is written in C# or Visual Basic, the circular references will stay within the CLR and the cycle will be garbage-collected. If the class is written in C++ (as in this case) or JavaScript, the cycle will cause a leak.

There are a few ways to ensure your app isn’t affected by leaks like this. First, you don’t need to worry about these leaks if you’re writing a pure JavaScript app. The JavaScript GC is often smart enough to track circular references across all WinRT objects. (See the previous article for more details on JavaScript memory management.)

You also don’t need to worry if you’re registering for events on objects you know are in the XAML framework. This means anything in the Windows.UI.Xaml namespace and includes all of the familiar FrameworkElement, UIElement and Control classes. The CLR GC is smart enough to track circular references through XAML objects.

The other way to deal with this type of leak is to unregister the event handler when it’s no longer needed. In this example, you could unregister the event handler in the OnNavigatedFrom event. The reference created by the event handler would be removed and all of the objects would get destroyed. Note that it’s not possible to unregister lambdas, so handling an event with a lambda can cause leaks.

Analyzing Memory Leaks in Windows Store Apps Using C# and Visual Basic

If you’re writing a Window Store app in C# or Visual Basic, it’s useful to note that many of the techniques discussed in the previous article on JavaScript apply to C# and Visual Basic as well. In particular, the use of weak references is a common and effective way to reduce memory growth (see bit.ly/S9gVZW for more information), and the “Dispose” and “Bloat” architecture patterns apply equally well.

Now let’s take a look at how you can find and fix common leaks using tools available today: Windows Task Manager and a managed code profiling tool called PerfView, available for download at bit.ly/UTdb4M.

In the “Event Handlers” section of the previous article on leaks, we looked at an example called LeakyApp (repeated in Figure 13 for your convenience), which causes a memory leak in its window’s SizeChanged event handler.

Figure 13 LeakyApp

public sealed partial class ItemDetailPage : LeakyApp.Common.LayoutAwarePage
  {
    public ItemDetailPage()
    {
      this.InitializeComponent();
    }
    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
      base.OnNavigatedTo(e);
      Window.Current.SizeChanged += WindowSizeChanged;
    }
    private void WindowSizeChanged(object sender,
      Windows.UI.Core.WindowSizeChangedEventArgs e)
    {
      // Respond to size change
    }
// Other code
  }

In our experience, this is the most common type of leak in C# and Visual Basic code, but the techniques we’ll describe apply just as well to circular event leaks and to unbounded data-structure growth. Let’s take a look on how you can find and fix leaks in your own apps using tools available today.

Looking for Memory Growth

The first step in fixing a memory leak is to identify steady memory growth from operations that should be memory neutral. In the “Discovering Memory Leaks” section of the previous article, we discussed a very simple way you can use the built-in Windows Task Manager to watch for the growth of the Total Working Set (TWS) of an app by running through a scenario multiple times. In the example app, the steps to cause a memory leak are to click on a tile and then navigate back to the homepage.

In Figure 14, the top screenshot shows the working set in Task Manager before 10 iterations of these steps, and the bottom screenshot shows this after 10 iterations.

Watching for Memory Growth

Figure 14 Watching for Memory Growth

After 10 iterations, you can see the amount of memory used has grown from 44,404K to 108,644K. This definitely looks like a memory leak, and we should dig further.

Adding GC Determinism

To be certain we have a memory leak on our hands, we need to confirm that it persists after full garbage collection cleanup. The GC uses a set of heuristics to decide the best time to run and reclaim dead memory, and it usually does a good job. However, at any given time there could be a number of “dead” objects in memory that haven’t yet been collected. Deterministically calling the GC allows us to separate growth caused by slow collection and growth caused by true leaks, and it clears the picture when we look to investigate what objects are truly leaking.

The easiest way to do this is to use the “Force GC” button from within PerfView, shown in the next section in the instructions on taking a heap snapshot. Another option is to add a button to your app that will trigger the GCs using code. The following code will induce a garbage collection:

private void GCButton_Click(object sender, RoutedEventArgs e)
{
  GC.Collect();
  GC.WaitForPendingFinalizers();
  GC.Collect();
}

The WaitForPendingFinalizers and subsequent Collect call ensure that any objects freed up as a result of finalizers will get collected as well.

In the example app, however, clicking this button after 10 iterations freed up only 7MB of the 108MB of the working set. At this point we can be pretty sure there’s a memory leak in LeakyApp. Now, we need to look for the cause of the memory leak in our managed code.

Analyzing Memory Growth

Now we’ll use PerfView to take a diff of the CLR’s GC Heap, and analyze the diff to find the leaked objects.

To find out where memory is being leaked, you’ll want to take a snapshot of the heap before and after you run through a leak-­causing action in your app. Using PerfView, you can diff the two snapshots to find where the memory growth is.

To take a heap snapshot with PerfView:

  1. Open PerfView.
  2. Click on Memory in the menu bar.
  3. Click Take Heap Snapshot (see Figure 15).
  4. Select your Windows Store app from the list.
  5. Click the “Force GC” button to induce a GC within your application.
  6. Set the filename for the dump you wish to save and click Dump GC Heap (see Figure 16).

Taking a Heap Snapshot
Figure 15 Taking a Heap Snapshot

Dumping the GC Heap
Figure 16 Dumping the GC Heap

A dump of the managed heap will be saved to the file you specified and PerfView will open a display of the dump file showing a list of all the types on the managed heap. For a memory leak investigation, you should delete the contents of the Fold% and FoldPats text boxes and click the Update button. In the resulting view, the Exc column shows the total size in bytes that type is using on the GC heap and the Exc Ct column shows the number of instances of that type on the GC heap.

Figure 17 shows a view of the GC dump for LeakyApp.

A Heap Snapshot in PerfView
Figure 17 A Heap Snapshot in PerfView

To get a diff of two heap snapshots showing the memory leak:

  1. Run through a few iterations of the action that causes the memory leak in your app. This will include any lazy-loaded or one-time initialized objects in your baseline.
  2. Take a heap snapshot, including forcing a GC to remove any dead objects. We’ll refer to this as the “before” snapshot.
  3. Run through several more iterations of your leak-­causing action.
  4. Take another heap snapshot, including forcing a GC to remove any dead objects. This will be the “after” snapshot.
  5. From the view of the after snapshot, click the Diff menu item and select the before snapshot as your baseline. Make sure to have your view opened from the before snapshot, or it won’t show up in the diff menu.
  6. A new window will be shown containing the diff. Delete the contents of the Fold% and FoldPats text boxes and update the view.

You now have a view that shows the growth in managed objects between the two snapshots of your managed heap. For LeakyApp, we took the before snapshot after three iterations and the after snapshot after 13 iterations, giving the difference in the GC heap after 10 iterations. The heap snapshot diff from PerfView is shown in Figure 18.

The Diff of Two Snapshots in PerfView
Figure 18 The Diff of Two Snapshots in PerfView

The Exc column gives the increase in total size of each type on the managed heap. However, the Exc Ct column will show the sum of the instances in the two heap snapshots rather than the difference between the two. This is not what you’d expect for this kind of analysis, and future versions of PerfView will allow you to view this column as a difference; for now, just ignore the Exc Ct column when using the diff view.

Any types that leaked between the two snapshots will have a positive value in the Exc column, but determining which object is preventing objects from being collected will take some analysis.

Analyzing the Diff

Based on your knowledge of the app, you should look at the list of objects in the diff and find any types you wouldn’t expect to grow over time. Look at types that are defined in your app first, because a leak is likely to be the result of a reference being held on to by your app code. The next place to look is at leaked types in the Windows.UI.Xaml namespace, as these are likely to be held on to by your app code as well. If we look first at types defined only in our app, the ItemDetailPage type shows up near the top of the list. It’s the largest leaked object defined in our example app.

Double-clicking on a type in the list will take you to the “Refered-From” (sic) view for that type. This view shows a reference tree of all the types that hold references to that type. You can expand the tree to step through all of the references that are keeping that type alive. In the tree, a value of [CCW (ObjectType)] means that the object is being kept alive by a reference from outside of managed code (such as the XAML framework, C++ or JavaScript code). Figure 19 shows a screenshot of the reference tree for our suspect ItemDetailPage object.

The Reference Tree for the ItemDetailPage Type in PerfView
Figure 19 The Reference Tree for the ItemDetailPage Type in PerfView

From this view you can clearly see that the ItemDetailPage is being held live by the event handler for the WindowSizeChanged event, and this is most likely the cause of the memory leak. The event handler is being held on to by something outside of managed code, in this case the XAML framework. If you look at one of the XAML objects, you can see that they’re also being kept alive by the same event handler. As an example, the reference tree for the Windows.UI.Xaml.Controls.Button type is shown in Figure 20.

The Reference Tree for the Windows.UI.Xaml.Controls.Button Type
Figure 20 The Reference Tree for the Windows.UI.Xaml.Controls.Button Type

From this view, you can see that all of the new instances of UI.Xaml.Controls.Button are being kept alive by ItemDetailPage, which in turn is being kept alive by the WindowSizeChangedEventHandler.

It’s pretty clear at this point that to fix the memory leak we need to remove the reference from the SizeChanged event handler to ItemDetailPage, like so:

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
  Window.Current.SizeChanged -= WindowSizeChanged;
}

After adding this override to the ItemDetailPage class, the ItemDetailPage instances no longer accumulate over time and our leak is fixed.

The methods described here give you some simple ways to analyze memory leaks. Don’t be surprised if you find yourself encountering similar situations. It’s very common for memory leaks in Windows Store apps to be caused by subscribing to long-lived event sources and failing to unsubscribe from them; fortunately, the chain of leaked objects will clearly identify the problem. This also covers cases of circular references in event handlers across languages, as well as traditional C#/Visual Basic memory leaks caused by unbounded data structures for caching.

In more complex cases, memory leaks can be caused by cycles between objects in apps containing C#, Visual Basic, JavaScript and C++. These cases can be hard to analyze because many objects in the reference tree will show up as external to managed code.

Considerations for Windows Store Apps That Use Both JavaScript and C# or Visual Basic

For an application that’s built in JavaScript and uses C# or Visual Basic to implement underlying components, it’s important to remember that there will be two separate GCs managing two separate heaps. This will naturally increase the memory used by the application. However, the most important factor in your app’s memory consumption will continue to be your management of large data structures and their lifetimes. Doing so across languages means you need to keep the following in mind:

Measure the Impact of Delayed Cleanup A garbage-collected heap typically contains collectible objects awaiting the next GC. You can use this information to investigate the memory use of your app. If you measure the difference in memory usage before and after a manually induced garbage collection, you can see how much memory was waiting for “delayed cleanup” versus the memory used by “live” objects.

For dual-GC apps, understanding this delta is very important. Due to references between heaps, it might take a sequence of garbage collections to eliminate all collectible objects. To test for this effect and to clear your TWS so that only live objects remain, you should induce repeated, alternating GCs in your test code. You can trigger a GC in response to a button click (for example), or by using a performance analysis tool that supports it. To trigger a GC in JavaScript, use the following code:

window.CollectGarbage();
For the CLR, use the following:
GC.Collect(2, GCCollectionMode.Optimized);
GC.WaitForPendingFinalizers();

You might have noticed that for the CLR we use only one GC.Collect call, unlike the code for inducing a GC in the section on diagnosing memory leaks. This is because in this instance we want to simulate the actual GC patterns in your application that will issue only one GC at a time, whereas previously we wanted to try and clean up as many objects as possible. Note that the PerfView Force GC feature shouldn’t be used to measure delayed cleanup, because it may force both a JavaScript and a CLR GC.

The same technique should be used to measure your memory use on suspend. In a C#- or JavaScript-only environment, that language’s GC will run automatically on suspend. However, in C# or Visual Basic and JavaScript hybrid apps, only the JavaScript GC will run. This might leave some collectible items on the CLR heap that will increase your app’s private working set (PWS) while suspended. Depending on how big these items are, your app could be prematurely terminated instead of being suspended (see the “Avoid Holding Large References on Suspend” section of the previous article).

If the impact on your PWS is very large, it may be worth invoking the CLR GC in the suspend handler. However, none of this should be done without measuring a substantial reduction in memory consumption, because in general you want to keep work done on suspend to a minimum (and in particular, nowhere near the 5 second time limit enforced by the system).

Analyze Both Heaps When investigating memory consumption, and after eliminating any impact of delayed cleanup, it’s important to analyze both the JavaScript heap and the .NET heap. For the .NET heap, the recommended approach is to use the PerfView tool, described in the “Analyzing Memory Leaks in Windows Store Apps Using C# and Visual Basic” section, whether you want to understand total memory consumption or to investigate a leak.

With the current release of PerfView, you’re able to look at a combined view of the JavaScript and .NET heaps, allowing you to see all objects across managed languages and understand any references between them.


Chipalo Street is a program manager on the Windows 8 XAML team. He started working on Windows Presentation Foundation (WPF) straight out of college. Five years later he’s helped XAML evolve through three products (WPF, Silverlight and Windows 8 XAML) and multiple releases. During Windows 8 development, he owned everything related to text, printing and performance for the XAML platform.

Dan Taylor is a program manager on the Microsoft .NET Framework team. In the past year, Taylor has been working on performance of the .NET Framework and the CLR for Windows 8 and Core CLR for Windows Phone 8.

Thanks to the following technical experts for reviewing this article: Deon Brewis, Mike Hillberg, Dave Hiniker and Ivan Naranjo

 

Rate: