Memory usage may be higher than necessary in Compact Framework applications
-
If you use more than one form, and you have a MainMenu control on the form - particularly if you actually use it as a menu (it's common to leave an empty menu bar in Pocket PC applications because otherwise you see the menu bar of the application beneath...) - your memory usage might be higher than is actually necessary. In Compact Framework 2.0, the
System.ComponentModel.Component
class implements a finalizer. In theDispose
method, it callsGC.SuppressFinalize(this)
to suppress finalization if you actually clean up after yourself. I've recommended this for a long time because of how finalization works (see later). For a desktop project, with the full .NET Framework, when you drop a component that implementsIComponent
onto a form, it creates acomponents
member (aSystem.ComponentModel.Container
) and adds the component to that collection, using a suitable constructor if available. It generates aDispose(bool)
override which checks whethercomponents
is null and if it's not, it callsDispose
on the collection, which will then dispose all the objects in the collection. The Smart Device forms designer, however, generates theDispose(bool)
method but never assigns thecomponents
field and doesn't otherwise generateDispose
calls for anything added to the form (controls are disposed when they receive theWM_DESTROY
message). This means that when the form is collected, the components end up on the finalizer queue. Finalizers work like this: there is a single finalizer thread per process which runs the finalizers. It checks a queue of objects to finalize and slowly proceeds through running their finalizers. When the garbage collector runs, any objects with (unsuppressed) finalizers that are no longer referenced are placed on the finalizer queue. The queue itself is considered a 'root' so objects that haven't yet been finalized are still considered referenced for GC, and any objects they're still referencing are also retained. Objects with a finalizer therefore take at least two GC collections for the memory to actually be freed. The particular problem we noticed is that theHardwareButton
class, theMainMenu
class and theMenuItem
class all derive from Component and therefore all have finalizers. Since they're not disposed they end up on the finalizer queue, and their event handlers are -
If you use more than one form, and you have a MainMenu control on the form - particularly if you actually use it as a menu (it's common to leave an empty menu bar in Pocket PC applications because otherwise you see the menu bar of the application beneath...) - your memory usage might be higher than is actually necessary. In Compact Framework 2.0, the
System.ComponentModel.Component
class implements a finalizer. In theDispose
method, it callsGC.SuppressFinalize(this)
to suppress finalization if you actually clean up after yourself. I've recommended this for a long time because of how finalization works (see later). For a desktop project, with the full .NET Framework, when you drop a component that implementsIComponent
onto a form, it creates acomponents
member (aSystem.ComponentModel.Container
) and adds the component to that collection, using a suitable constructor if available. It generates aDispose(bool)
override which checks whethercomponents
is null and if it's not, it callsDispose
on the collection, which will then dispose all the objects in the collection. The Smart Device forms designer, however, generates theDispose(bool)
method but never assigns thecomponents
field and doesn't otherwise generateDispose
calls for anything added to the form (controls are disposed when they receive theWM_DESTROY
message). This means that when the form is collected, the components end up on the finalizer queue. Finalizers work like this: there is a single finalizer thread per process which runs the finalizers. It checks a queue of objects to finalize and slowly proceeds through running their finalizers. When the garbage collector runs, any objects with (unsuppressed) finalizers that are no longer referenced are placed on the finalizer queue. The queue itself is considered a 'root' so objects that haven't yet been finalized are still considered referenced for GC, and any objects they're still referencing are also retained. Objects with a finalizer therefore take at least two GC collections for the memory to actually be freed. The particular problem we noticed is that theHardwareButton
class, theMainMenu
class and theMenuItem
class all derive from Component and therefore all have finalizers. Since they're not disposed they end up on the finalizer queue, and their event handlers areFirst of all thanks for this, secondly, do you know if this applies to CF 1.0 as well? We have some clients that are stuck with PocketPC 2002 for a bit, and so we're still on the CF 1.0 platform.
This statement was never false.
-
First of all thanks for this, secondly, do you know if this applies to CF 1.0 as well? We have some clients that are stuck with PocketPC 2002 for a bit, and so we're still on the CF 1.0 platform.
This statement was never false.
CF 1.0 is a bit different. I don't know of any way to actually view the object graphs or find out if the finalizer mechanism is the same as the desktop framework and CF 2.0. However, this blog post[^] suggests that, as elsewhere, the objects requiring finalization are placed on a separate queue processed by a background thread. System.ComponentModel.Component does not have a finalizer, so classes deriving from it do not automatically get a cleanup penalty as happens in CF 2.0. It does not implement the IDisposable interface, though, nor a Dispose method, although it does have a protected virtual Dispose(bool) method. All this does is to fire the Disposed event. In addition, System.ComponentModel.Component does not implement the IComponent interface! That means you can't add them to System.ComponentModel.Container, so you can't take advantage of the container's support for disposing all of its contained components when it is disposed. This makes Container completely useless so they should have omitted it. However, many of the components that derive from Component have their own finalizer. MainMenu falls into this category. However, it does not provide a Dispose method and doesn't implement IDisposable. The best you can hope for is to disconnect it from the form, by setting the form's Menu property to null, then you can manually suppress finalization with GC.SuppressFinalize(). You really have to inspect the implementation (using Reflector[^], for example) to see how to clean up one of these classes appropriately. Using Reflector just on the assemblies that ship with .NET Compact Framework 1.0 SP3, I see that the following classes derive from Component and have finalizers: Microsoft.WindowsCE.Forms.InputPanel System.Windows.Forms.ImageList System.Windows.Forms.ContextMenu System.Windows.Forms.MainMenu System.Windows.Forms.Timer so these are all classes to watch out for. The SqlConnection, SqlCeConnection etc. classes also derive from Component but they do implement IDisposable, and it's a lot less common to drop them on forms. Of these, ImageList is little bother because it doesn't have any events or references to anything else, so it can't hold anything else in memory. It's d