Automatic inspections
To ease your life, dotMemory automatically checks the snapshot on most common types of memory issues. These inspections could be a great starting point in analyzing a snapshot if you don't know where to begin. To open the Inspections view, click the corresponding tab in the snapshot analysis tab. Along with the automatic inspections, the view provides diagrams that show memory usage and the fragmentation of the managed heap segments.
Memory usage diagrams
The view provides two diagrams that show memory usage in the snapshot:
Largest Size Diagram
This diagram answers the question "What objects take the major part of memory in my app?" It shows how much memory is occupied by objects of a specific type. Clicking an item on the diagram makes it an analysis subject leading you to the Group by Types view.
Largest Retained Size Diagram
This diagram answers the question "What are the key objects in my application?" It shows the list of dominators – main objects that retain all other app objects in memory. Clicking an item on the diagram makes it an analysis subject leading you to the Group by Dominators view.
Automatic inspections
String duplicates
Repeatedly creating strings with the same value instead of reusing the existing one wastes memory. dotMemory detects duplicated strings and shows how much memory is wasted.
To analyze the objects
Click the link in the inspection header or double-click a particular object set in the list.
To fix the issue
If strings with the same value waste a huge amount of memory or generate significant traffic (for example, if your app parses text input) consider implementing string interning.
Sparse arrays
Sparse arrays are arrays that are mostly filled with zero elements. Sparse arrays are inefficient from the perspective of performance and memory usage. dotMemory automatically finds sparse arrays and shows you how much memory is lost (occupied by zero values) because of them.
To analyze sparse arrays
Click the link in the inspection header or double-click a particular object in the list.
Finalizable objects
Finalizable objects are the objects that use the Finalize()
method to release unmanaged resources. The problem of using this pattern is, first, that the lifetime of finalizable objects is extended by at least one more GC cycle and, second, that the finalization thread (that executes the Finalize()
method) is run unpredictably. This may cause problems in case you want to reclaim the released resources as quickly as possible and may lead to sudden performance drops. dotMemory detects and shows all objects queued for finalization and objects finalized since the previous snapshot.
To analyze finalizable objects
To fix the issue
Implement the
IDisposable
interface for the type that causes issues and release all unmanaged resources via itsDispose()
method. For more information about the dispose pattern, refer to Microsoft Learn.
Event handlers leak
Such a leak occurs when you subscribe an object (let's call it listener) to an event of some other object (let's call it source). For example: Timer1.Tick += OnTimer;
During subscription, the source object gets a reference to the event handler of the listener object. If you delete the listener, this reference will prevent it from being garbage collected. dotMemory automatically finds objects that are referenced in event handlers but are never unsubscribed from corresponding events.
To analyze the objects
Click the link in the inspection header or double-click a particular object in the list.
To fix the issue
Unsubscribe the listener from the event when it is no longer needed. For example:
Timer1.Tick -= OnTimer;
WPF binding leak
Breaking WPF data binding patterns also can cause a memory leak. After you perform data binding to some property of a source object, the binding target object starts to listen for property change notifications. If the property is not a DependencyProperty
object and the target object doesn't implement the INotifyPropertyChanged
interface, a memory leak in the source object and in every object to which the source object refers may occur. dotMemory detects such binding pattern violations and shows you the list of objects that may potentially cause this leak type.
To analyze the objects
Click the link in the inspection header or double-click a particular object in the list.
To fix the issue
Make the source object implement the
INotifyPropertyChanged
interface or remove binding when it is no longer needed using theClearBinding
method.
WPF collection binding leak
This leak is similar to the WPF binding leak described above. If there is binding to a collection, that doesn't implement the INotifyCollectionChanged
interface, WPF creates a strong reference to this collection. As a result, it stays in memory for the entire application lifetime. dotMemory detects and shows you such objects.
To analyze the objects
Click the link in the inspection header or double-click a particular object in the list.
To fix the issue
Make the source collection implement the
INotifyCollectionChanged
interface. Another way is to use theObservableCollection
collection as it already implements theINotifyCollectionChanged
interface.
Dependency property leak
Such leaks occur due to the same reasons as the event handlers leak. Garbage Collector will not collect objects subscribed on DependencyProperty
changes through the AddValueChanged
method until they're unsubscribed using the RemoveValueChanged
method. dotMemory detects and shows you all such objects.
To analyze the objects
Click the link in the inspection header or double-click a particular object in the list.
To fix the issue
When the lifetime of a subscribed object is over, take care of unsubscribing it using the
RemoveValueChanged
method.
x:Name WPF leak
Such leaks take place because of the following WPF peculiarity: WPF creates a strong global reference to the UI element that is declared in XAML and uses the x:Name directive. For example:
Thus, if you dynamically remove the element declared in such a way, it will still be in memory.
To analyze the objects
Click the link in the inspection header or double-click a particular object in the list.
To fix the issue
One way to remove the leak is to declare the UI element in the C# code instead of XAML. Another way is to call the
UnregisterName
method of the parent control when you want to remove the UI element. For example:this.UnregisterName("myControl1");
Heap fragmentation
Use the Heap Fragmentation diagram to evaluate the fragmentation of the managed heap segments:
Generation 1 and 2 heap segments.
Large object heap (LOH) – a separate heap for large objects (85,000 bytes and larger). LOH fragmentation can be a serious issue .
Pinned object heap – a separate heap for objects that are prohibited from moving within the heap. You can open pinned objects as a separate object set.
Frozen object heap (FOH) – a separate heap for immutable objects.
Clicking the diagram header will open the Group by Generations view for all objects in the snapshot.
Heap segment name.
Number of heaps within a segment.
Each bar on the diagram represents a certain heap.
Total heap segment size defined by GC.
The length of bars on the diagram corresponds to the total size of certain heaps within a segment. Note that the total size may be slightly larger than the sum of pinned, unpinned, and free: The total size also includes memory blocks used for alignment, padding, and other specific purposes.
Total size of pinned objects.
Pinned objects are prohibited from moving within the heap. Typically, such objects are used by some unmanaged code or may be a result of using the
fixed
statement.Total size of all objects (excluding pinned objects) allocated in the heap segment.
Total size of free memory in the heap segment.
Heap Fragmentation also lets you open a specific object set – Pinned objects.