NET Memory Profiler
NET Memory Profiler
NET Memory Profiler
by SciTech Software AB
This manual was produced using ComponentOne Doc-To-Help.
Contents
User Manual 1
Introduction...................................................................................................... 1
How to.............................................................................................................. 2
Profile a .NET Process ........................................................................ 2
Start Profiling an Application ............................................................ 4
Start Profiling ASP.NET ..................................................................... 4
Start Profiling a Windows Store App .................................................5
Start Profiling a .NET Core App ........................................................ 6
Start Profiling an ASP.NET Core App using IIS Express and .NET
Core.......................................................................................................7
Start Profiling a Silverlight Application ............................................ 8
Start Profiling a WPF Browser Application ...................................... 9
Start Profiling a Windows Service ..................................................... 9
Select Processes to Profile .................................................................. 9
Profile a Remote Process...................................................................10
Use the Memory Analyser ................................................................. 11
Find Memory Leaks ........................................................................... 12
View Real-time Heap Utilization ...................................................... 13
Work with a Previously Saved Session ............................................. 15
Find out Information about the Native Memory of a .NET
Process................................................................................................ 15
Perform Unit Testing together with the Profiler ............................. 16
Increase the Performance of the Profiler ......................................... 19
Attach to a Process............................................................................ 20
Import Memory Dump Files ............................................................. 21
Create or Load a Profiler Project ..................................................... 22
Start Profiling using a Profiler Project ............................................ 23
Compare Snapshots using the Profiler Projects Explorer.............. 24
Setup Native Stack Walks ................................................................ 24
Starting .NET Memory Profiler .................................................................... 25
Interactive Mode............................................................................... 25
Non-Interactive Mode ...................................................................... 25
Command Line Arguments .............................................................. 26
Available Command Line Arguments ..............................................27
Response Files .................................................................................. 30
Exit Codes........................................................................................... 31
Automatic Memory Analysis ......................................................................... 31
Issue Presentation ............................................................................ 33
Ignoring Analysis Issues .................................................................. 35
Modifying Ignored Issues..................................................................37
Analysis Issues List........................................................................... 39
Appendix 215
Attach to Process using Profiling API ......................................................... 215
Support for Generics .................................................................................... 215
Generic Types................................................................................... 215
Generic Methods..............................................................................216
Known Issues................................................................................................216
Index 221
Introduction
.NET Memory Profiler is a tool for the .NET Common Language
Runtime that allows the user to retrieve information about all
instance allocations performed on the garbage collected heap (GC
heap) and all instances that reside on the GC heap. The retrieved
information is presented in real time, both numerically and
graphically.
The real-time information presented gives a good overview of the
activity and status of the GC heap, but by collecting snapshots of
the heap, very detailed information can be presented. Information
is provided about: all managed types, all instances of types, the call
stacks of the instance allocations, and the path to a root from each
instance. An instance graph can be presented for a set of instances
or a specific instance, giving a visual overview of how the instances
are used and how they are related to other instances.
It is possible to compare two snapshots, which is a very convenient
and efficient way of detecting memory leaks in a program. The
memory profiler also helps to locate sections of the program that
perform excessive allocations.
The automatic memory analyzer provides tips and warnings
regarding common memory usage issues, making it easier to locate
potential memory leaks and optimize memory and resources
usage.
The profiler contains a dispose tracker, which provides additional
data about disposable instances (i.e. instances of classes
implementing IDisposable), and a heap utilization tracker, which
gives information about how the heap memory is utilized.
In addition to presenting information about the GC heap, the
profiler also presents information about the native memory of the
profiled process or any other process running on the computer.
How to...
This section describes how to perform some standard tasks with
.NET Memory Profiler.
or
Attach to a Process
Professional and Instead of having the profiler launch the process to profile, it is
Enterprise only also possible to attach the profiler to an already running .NET
Process.
There are two ways of attaching to a process: Attach using
profiling API and Inspection only attach. Attach using
profiling API allows object instances to be tracked between
snapshots, which allows the profiler identify new and removed
instances, which makes it easier to identify memory leaks.
However, when using the profiler API, the profiler will be
loaded into the process and this will affect the performance and
memory usage of the profiler process. Inspection only
attach on the other hand, only inspects the profiled process by
investigating the memory in the process. This has the
advantage that the profiled process is not affected by the
profiler, but the profiler cannot track instances and how they
are moved by the garbage collector.
For more information about profiling API attach, see Attach to
Process using Profiling API on page 215.
The following steps should be performed to attach the profiler to a
running process:
If you are running the profiler under Visual Studio 2005 and a
profiler project is set as active, you can right click a project in
the Solution explorer and select Start using Profiler
Project as well.
Interactive Mode
When running the profiler in interactive mode, the standard user
interface of the profiler is shown when it is started.
Unless the console version of the profiler has been started, or the
command line argument /noui has been provided, the profiler
always runs in interactive mode.
Non-Interactive Mode
The non-interactive mode should only be used when doing
automated testing, for instance, as part of a script. In non-
interactive mode, no user interface window is shown, and it is not
possible to manually control the profiler. The profiler is started in
non-interactive mode by supplying the /noui argument on the
command line or by running the console version of the profiler
(NetMemProfilerConsole.exe).
Response Files
Response files can be used to provide command-line arguments to
the profiler. A response file is a text file with one command line
argument per line. A # symbol can be used to specify comments
in the response file.
/noui
/disposetracker-
/sessionprompt-
/sessionfile:MemUser.exe session %DATE% %TIME%.prfsession
/sessionfolder:C:\Autosaved sessions
To start the profiler using the response file above, the following
command should be used:
NetMemProfiler.exe @MemUser.rsp
Exit Codes
The .NET Memory Profiler executables return an exit code. This
exit code can be useful when doing automated testing in a script.
The following return codes are used:
Code Meaning
0 Success. The profiler was run normally.
1 Memory leak detected. A memory leak has been detected when
profiling.
2 Argument error. The arguments supplied contained an invalid
argument.
3 Unexpected error. The profiler exited because of an unexpected
error.
259 Returned if the /passthrough command line argument was
provided and no return value from the profiled process exists
(e.g. if the process was terminated). (The value 259 is defined as
STILL_ACTIVE. in the Windows SDK and is used by
GetExitCodeProcess to indicate that the exit code is not
available)
Any If the /passthrough command line arguments was provided, the
exit code of the profiled process is returned.
Serious warning
A serious warning indicates an issue that could, with a high
probability, cause memory usage problems, such as memory or
resource leaks.
Warning
A warning indicates an issue that could cause memory usage
problems, such as memory or resource leaks.
Minor warning
A minor warning indicates an issue that could cause bad
memory or resource utilization, but it should not cause
problems like memory leaks.
Indirect warning
An indirect warning indicates an issue that is caused by
another warning issue. The issue will contain information
about the issue that causes the direct warning.
Information
The information level indicates issues that provide information
about memory usage, but they are not directly related to a
memory usage problem.
Indirect information
The indirect information level indicates that an issue is a
consequence of another informational issue.
Pending issue
The pending issue level indicates that there is a possible issue,
but that further analysis has to be performed. This level is
currently only used for duplicate instances that are not fully
investigated. For more information, see Duplicate Instances
Detection on page 47.
An issue can be related to a specific instance (e.g. a potential
memory leak), a specific type (e.g. a type with undisposed
instances), or the full snapshot memory data (e.g. a summary of
the total ASP.NET cache usage).
Issue Presentation
As mentioned previously, issues are presented in the info panels of
the Types/Resources page, the Type details page, and the
Instance details page. Unless the info panel is expanded, only
the first, most important, issue is presented, and only the title is
included.
Overview page
In the Overview info panel, a summary for each issue is
presented.
For instance-specific issues, the summary will contain links to the
types that have instances related to the issue. For type-specific
issues, links to the types related to the issue will be listed. Further
information can be retrieved by clicking the provided links.
A tooltip will also be provided for each type with associated
analysis issues. The tooltip is shown by hovering over the analysis
icon for the type, and the information presented is the same as in
the Type/Filter details info panel (described below).
Ignore issue in
The Ignore issue in dropdown list is used to define where the
information about the ignored issue should be stored. The
available options are:
All sessions
The ignored issue will be stored as a default ignored issue. It
will be ignored in all profiling sessions.
Profiler project (Professional and Enterprise only)
The ignored issue will be stored in the profiler project
associated with the session. It will be ignored in all profiling
sessions associated with the same project. This option is only
available if the session is associated with a profiler project.
Current session
The ignored issue will be stored in the current profiler session.
It will only be ignored in this session.
Error
WPF binding source memory leak
An instance that is the source of a WPF binding where
the instance class does not implement
INotifyPropertyChanged has been detected. The target
of the binding is reachable from the source instance,
which will cause a memory leak. To fix this issue,
INotifyPropertyChanged should be implemented for
the type, or the binding should be changed to a
OneTime binding. To find out how the source and
target are connected, use the Expand path link in the
issue information. For more information, see Microsoft
KB938416.
WPF binding target memory leak
Same as WPF binding source memory leak but
this issue is assigned to the target of the binding.
Serious warning
Potential memory leak
An instance has been detected as a potential memory
leak by a memory assertion. For more information
about memory assertions, see Use Assertions to Detect
Memory Leaks on page 199. This issue cannot be
ignored. To remove this issue, either correct the
memory leak, or, if it is a falsely identified memory
leak, modify the memory assertion so that the instance
is not included.
Disposed instance with direct EventHandler
roots
A disposed instance is directly rooted by an
EventHandler, i.e., the instance is only used as the
target of an EventHandler and it cannot be reached
from any other root without passing through a delegate.
Since a disposed instance should no longer be used and
Minor warning
Direct delegate roots
An instance is directly rooted by a delegate, i.e., the
instance is only used as the target of a delegate and it
cannot be reached from any other root without passing
through a delegate. Since delegates are a common cause
of memory leaks, this issue can indicate a memory leak.
Investigate the instance and the delegate instance to
find out whether the instance is unintentionally held by
the delegate, or if the issue can be ignored.
Delegate roots via closure
Same as Direct delegate roots, but the instance is
held by a closure instance, and it is the closure instance
that has a direct delegate root. This can occur when
Indirect warning
Disposed instance with indirect EventHandler
roots
A disposed instance is held by another instance that is
directly rooted by an EventHandler. A link will be
provided to the he instance that is directly rooted by the
EventHandler instance. Investigate the directly rooted
instance to resolve this issue.
Indirect EventHandler roots
An instance is held by another instance that is directly
rooted by an EventHandler. A link will be provided to
the he instance that is directly rooted by the
EventHandler. Investigate the directly rooted instance
to resolve this issue.
Disposed instance with indirect delegate roots
A disposed instance is held by another instance that is
directly rooted by a delegate. A link will be provided to
the he instance that is directly rooted by the delegate.
Suggestion
WPF binding target with source that does not
implement INotifyPropertyChanged
An instance, which is the source of a WPF binding
where the instance class does not implement
INotifyPropertyChanged, has been detected. This
delays the garbage collection of the involved instances
and increases the risk of a memory leak.
To fix this issue, INotifyPropertyChanged should be
implemented for the type, or the binding should be
changed to a OneTime binding. For more information,
see Microsoft KB938416.
WPF bindings with source that does not
implement INotifyPropertyChanged
Same as WPF binding target with source that
does not implement INotifyPropertyChanged
but this issue is assigned to the target of the binding.
Undisposed instances (perform action)
A disposable instance has been garbage collected
without being properly disposed. Based on the
classification of disposable types, disposing the instance
might have performed some exit or cleanup operation.
This operation can for instance be flushing data to a
file, committing or reverting a transaction, clearing
security buffers, or deleting temporary files. For more
information about disposable types classification and
undisposed instances, see Disposable Types
Classification and Undisposed Instances on page 51.
Undisposed instances (memory/resource
utilization)
A disposable instance has been garbage collected
without being properly disposed. Based on the
Information
ASP.NET session state summary
This issue presents summary information about
ASP.NET session states. This includes information
about how many session state instances exists, how
many instances are held by the session state, and how
many bytes are used by the held session state instances.
Links are provided to the session state types, and all
other types that have instances stored as session state.
ASP.NET session key
An instance is used as, or is part of, an ASP.NET
session state key.
ASP.NET session value
An instance is used as, or is part of, an ASP.NET
session state value.
ASP.NET session state instance
An instance acts as a session state container. This is
normally an instance of the InProcSessionState
class.
ASP.NET cache summary
This issue presents summary information about
ASP.NET cache. This includes information about how
many instances are held by the cache and how many
bytes are used by the held cache instances. Links are
provided to the types that have instances stored in the
cache.
ASP.NET cache key
An instance is used as, or is part of, an ASP.NET cache
key.
ASP.NET cache value
An instance is used as, or is part of, an ASP.NET cache
value.
Duplicate instances
An instance is part of a set of duplicate instances.
For more information, see Duplicate Instances
Detection on page 47.
FixedAddressValueType instance
Pending issue
Possible duplicate instances
A set of instances are possibly duplicates of each other,
but a full analysis of the instance graphs has not been
performed. To get information about whether the
instances are actually duplicates, use the Detect
duplicates link. For more information, see
Duplicate Instances Detection on page 47.
#3 #4 #5 #6
value: 6 value: 7 value: 6 value: 7
#1 #2
value: 5 value: 5
#3 #4 #5
value: 6 value: 7 value: 6
#1 #2
value: 5 value: 5
#3
value: 7
m_field2 m_field2
m_owner
#1 #2
value: 5 value: 5
#4 #5 #6
value: 6 m_field2 value: 6 value: 6
m_field2
m_field2
#1 #2 #3
value: 5 value: 5 value: 5
#7 #8
value: 7 value: 7
#4 #5 #6
value: 6 m_field2 value: 6 value: 6
m_field2
m_field2
#1 #2 #3
value: 5 value: 5 value: 5
The instances #2, #3, #5, #6, and #8 are considered additional
instances, and the sum of the instance sizes will be presented as
the additional bytes value for this set.
Held Duplicates
If a set of duplicates are found, it is often the case the duplicate
instances have references to other duplicate instances. For
instance, a duplicate List<> instance will have a reference to a
duplicate array instance (that contains the actual list data). When
investing a duplicate like this, it is usually better to focus on the
duplicated List<> instance and not the array, since the
duplication of the array is just a side effect.
In an attempt to identify these side effects, the profiler will provide
information about duplicate sets that are fully held by another
duplicate set. A set is fully held by another set if it is not possible to
reach any instance in the second set without passing through an
instance in the first set.
#7 #8
value: 7 value: 7
#4 #5 #6
value: 6 m_field2 value: 6 value: 6
m_field2 m_field2
m_owner m_owner m_owner
m_field1 m_field1 m_field1
#1 #2 #3
value: 5 value: 5 value: 5
Held by instance #1
In the graph below, the instances that are held by instance #1 are
colored red.
It can be noted that the reachable instances are the same as the
reachable instances from instance #1. This happens because
instance #5 references instance #1. If two instances have
references to each other, the reachable instances will by definition
be the same. Compare this with the next graph.
Held by instance #5
In the graph below, the instances that are held by instance #5 are
colored red.
#6 #7 #8
#4 #5 m_parent
m_parent
m_commonData
m_data m_child1 m_commonData
m_child1
m_data
m_data2 #1 #2 #3
m_commonData
Graph Presentation
The instance graph is presented as a directed graph, where each
instance and root is represented as a node, and the references
between the instances are represented by directed edges. The table
below presents the symbols that are used to present different types
of instances and roots.
Selected instance
Held instance
An instance that is held by the selected
instance.
Reachable instance
An instance that is reachable from the
selected instance, but it is not held.
Referrer instance
An instance that has a path of
references to the selected instance, but
it has not been identified as a root-
path instance.
Root
A root that is preventing the selected
instance from being garbage
collected.
Secondary item
This outline indicates a secondary element
of an operation. For instance, instances that
contain left-over instances from an expand
operation will be marked as secondary
instances.
The info window will present information about the element. The
actual information presented depends on the type of the element.
For example, for a single instance element includes a subset of the
Graph Synchronization
The instance graph is synchronized with the References,
Referenced by and Root paths views under the Type instance
details page. This is a one-way synchronization that synchronizes
changes in the other views with the graph. The following
synchronizations are performed:
Root path highlighting
When a root path is selected in the Root paths view, the
same root path is highlight in the graph
Selection tracking
When an instance is selected in the References tree or
the Referenced by tree, it is also highlighted as a
primary element in the graph (if it is included in the
graph).
Expand tracking
When an instance reference is expanded in the
References tree or the Referenced by tree, the same
instance is expanded in the graph (if it is part of a
combined instance node).
Tasks Window
The Tasks window is mainly used to provide access to the profiling
guides.
If profiling has been started using a guide, the selected guide is
automatically expanded and activated in the Tasks window. Other
available guides are also presented, but they are collapsed and
inactive. A profiling guide can be activated by using the drop-down
button to expand it. This will stop any active guide, and activate
the new one. Only a single guide can be active at the time.
Above the guides, quick access to common profiling commands is
available. The commands include: Collect snapshot, Stop
profiling, and Show real-time data.
A filter can also be created for the instances that are part of an
analysis issue by using the Show details link next to the issue
title.
Predefined Filters
In addition to the filters that can be created by Show details and
Show derived there is a set of predefined filters. Most of them are
included in the filters list on the Overview page. The Manage filters
command can be used to define the filters that should be included
on the Overview page. For more information, see Saving and
Managing Filters on page 76.
The following predefined filters are available:
Directly rooted instances
All instances and allocations
Remove filter(s)
This button is used to remove the filters selected in the filters
table.
Peak Snapshots
Profiler Projects
Professional and A .NET Memory Profiler project is used to define the process to
Enterprise only profile and to specify the session settings, making it easier to
use different settings for different applications. It also keeps
track of session files related to the project, allowing you to
compare snapshots between different sessions. This is
particularly useful when doing automated testing and you want
to check whether the memory usage has changed between
different builds of a program.
Profiler projects are created using the New Profiler project
command and loaded using the Open->Profiler Project
command. For more information see the How to topic Create
or Load a Profiler Project on page 22.
All created and loaded profiler projects are included in the
Profiler Projects Explorer window.
Remote Profiling
Enterprise only Remote profiling allows you to use a development computer to
profile a process running on a computer where you do not want
By the default the profiler tries to use the credentials of the current
Windows account, but this window provides the possibility to
enter the credentials of an alternate user. To enter the name of a
user on a separate domain, use the format <domain>\<user
name>.
The menu and the toolbar provide access to all the available
commands.
Each session document includes a process and snapshot selector at
the top and a set of tab pages. The tab pages are used to present
different views of the contents of the GC heap, both snapshot
information and real-time information. Additionally, information
about call stacks and native memory is presented. The available
tab pages are:
Start
The Start page provides quick access to common
tasks, such as starting a profiling session, starting
guided profiling, and opening a previous session
file. It will be hidden as soon as an active session is
started or a session is loaded. Use the New session
command to get back to the Start page.
Overview (For more information, see Overview Page on
page 113.)
Type/Resource/Filter details (For more information,
see Type/Resource/Filter Details Page on page 119.)
Instance details (For more information, see Instance
Details Page on page 128.)
Call stacks/methods (For more information, see Call
Stacks/Methods Page on page 136.)
Native memory (For more information, see Native
Memory Page on page 146.)
Commands
Commands on the File menu:
Status Bar
Field Sets
The Show [Field set] command in the toolbar or menu allows
you to select how information should be presented.
A field set defines which columns are included in the types list
(snapshot and real time), how call stacks and root paths can be
sorted, and how the fields are presented in the info panels.
Four different sets are available:
Standard
The information presented using this set consists of the
basic information about instances, e.g. number of
instances, instance sizes and the number of allocations
performed.
This is the only field set that is available when attaching
to a process or when comparing snapshots from
different sessions.
Dispose Info
This field set should be used to present information
collected by the dispose tracker. When using this set,
the instance size and allocation information presented
by the standard set is replaced with information about
disposed and undisposed instances. This set is only
available if the dispose tracker is enabled. For more
information, see Dispose Tracker on page 169.
Heap Utilization
This field set is used to present information about the
heap utilization. When using this set, the instance size
information presented by the standard set is replaced
by information about the distribution of allocations
over the different GC heaps (generation #0, #1, #2 and
the large heap). For more information, see Heap
Utilization Tracker on page 172.
Memory Leaks
This field set is only available if a memory assertion has
failed. It is used to present information about instances
that have been identified as potential memory leaks by
a memory assertion. When using this set, the instance
size and allocation information presented by the
standard set are replaced with information about the
potential memory leak instances.
Context Menus
Column Chooser
This command brings up the column Customization window.
Using this window it is possible to drag and drop columns (and
bands) to and from the column header.
Reset layout
This command can be used to restore the layout to the default
layout defined in the .NET Memory Profiler installation. This will
affect all column layouts, and the positioning of tool windows like
the Project explorer.
Preferences
The Preferences page is used to edit settings related to the user
interface of .NET Memory Profiler. The preferences are edited by
clicking Options under the Tools menu. This will bring up the
Options dialog, which is used to edit preferences and default
session settings. For more information about session settings, see
the next section.
Field values presentation style
This setting indicates how field values should be presented in the
Instance details view. The available options are:
Visual Studio
Session Settings
When a new profiling session is started, the settings for the session
are retrieved either from the associated project or from the default
settings if the session is not associated with a project. An active
The profiling levels are used to easily define session setting, based
on the amount of information to collect from the profiled
application versus the performance of the profiler. The Medium
profiling provides a good balance between the amount of data
collected and the performance impact of the profiler. When
profiling a very complex and memory consuming application, the
Low or Very low level might be more appropriate, as the memory
and performance overhead is much lower. To get full memory
information, including heap utilization, use the High level.
The profiling level is actually not a session setting itself, changing
the profiling level will just initialize a set of other session settings,
namely: Instance tracking level, Enable dispose tracker,
Collect allocation call stacks, Collect real-time data,
Enable heap utilization tracking, and Disable inlining. For
more information about these settings, see Instance Tracking
Settings Page on page 101 and General Settings Page on page
102.
The following levels are available:
Very low
No instance tracking, no dispose tracker, no allocation
call stacks collection, no real-time date, no heap
utilization, inlining not disabled.
Low
Limited instance tracking, no dispose tracker, limited
allocation call stacks collection (no instance specific call
stacks), limited real-time data (no total instances
information, just allocs/sec and live instances
information), no heap utilization.
Medium
Full instance tracking, dispose tracker enabled, full
allocation call stack information, real-time data
collection, no heap utilization.
High
Full instance tracking, dispose tracker enabled, full
allocation call stack information, real-time data
collection, heap utilization tracking enabled.
The Session file page is used to decide how session files should be
created.
Ask user if session file should be created
If this option is selected, a dialog will be shown, allowing the user
to decide whether the session should be saved or discarded
If the When saving session, allow the user to select
snapshots to save setting is checked, a dialog allowing the user
to select snapshots to include will appear when saving the session.
This is useful especially when Automatically save heap
snapshots is enabled, since more snapshots than necessary might
have been saved to the temporary file.
Save session
If this option is selected, no user interaction is needed and the
session file will be saved to a pre-defined location. The location is
defined by the Save to folder textbox and the Session file
name textbox. Clicking the Browse button will bring up a folder
browser, which can be used to select the save to folder. The file
name can include the strings %DATE% and %TIME%. These
strings will be replaced with the current date and time when the
session is saved.
Note that the session will only be saved if at least one snapshot has
been collected or if real-time data have been collected
Do not save session
If this option is selected, then all information collected by the
session will be discarded.
Settings
Overview Page
The Overview page is the main snapshot view. After a heap
snapshot has been collected, it shows information about all
managed types and unmanaged resources in the profiled
application. If more than one snapshot has been collected,
information about the changes between snapshots will also be
presented in this view.
You can use the Show snapshot and Comparison snapshot
dropdown lists to select which snapshots to view.
Show hierarchical
If the Show hierarchical check box is checked, the types and
resources are presented hierarchically in the Types/Resources
table, based on assembly/module and namespace. Otherwise, the
types and resources will be presented in a flat list.
Show types/resources
The Show types/resources dropdown list can be used to filter
the type and resources that should be included in the table below.
The following options are available:
All
With new or removed instances
With live and/or allocated instances
With live instances
With allocated instances
With disposed and/or undisposed instances
Types/Resources Table
The Types/Resources table presents a list of types and resources in
the profiled process. The items can be sorted by clicking on the
column headers and filtered using the column filter at the top row
and the Show types/resources dropdown list.
Instances
The Instances panel contains a list of the instances related to the
selected item is shown.
The details of an instance can be viewed by double-clicking an
entry in the Instances table or using the Show details
command.
The list can be sorted by clicking on the column headers. The
following information is presented in the instance table:
Analysis issue
Indicates if the memory analyser has found an issue
related to the instance. Further information about the
issue is presented in the tooltip of the issue icon. For
more information about analysis issues, see Automatic
Memory Analysis on page 31.
New instance
This column indicates whether the instance is new, i.e.,
the instance did not exist in the comparison snapshot.
If the new instance symbol is shown, then this instance
is new; otherwise, it existed in the comparison snapshot
as well.
Disposed instance
This column is only included for disposable types and
indicates whether the instance has been disposed. If the
disposed instance symbol is shown, then this instance
has been disposed.
Type
This column shows the name of the grouped type.
Live instances
This column shows the total number of live instances of
the grouped type.
Live bytes
This column shows the total number of bytes used by
the live instances of the grouped type.
Max held instances
This column shows the maximum number of held
instances by any instance of the grouped type. This
column is not included by default, but can be included
by customizing the columns.
Max held bytes
This column shows the maximum number of held bytes
by any instance of the grouped type.
Max reachable instances
This column shows the maximum number of reachable
instances by any instance of the grouped type. This
Group by type
The group by type check box is available under the filter instances
list. If selected the filter instances will be grouped by type.
Allocation Stacks
Under the Allocation stacks panel, the allocation call stacks
related to the type or resource are shown. The call stacks are
presented in the same way as under the Call Stacks/Methods page,
with the difference that only call stacks related to the selected type,
resource, or filter are presented. For more information about call
stacks presentation, see Call Stacks View on page 142.
Show details
This button can be used to show the details for the selected call
stack. The details are shown as an instance and allocation filter.
NOTE! Even though the actual fields that contains the reference
from one instance to another is presented in the root path, the
field is not considered when looking for root paths to combine. But
when two root paths are combined, the fields will also be
combined.
Instances graph
The Instances graph panel is used to present a graph of the
instances selected in the Instances list, the instances of the
selected Shortest root path, or the instances of the selected
Allocation stack. For more information about the instance
graph, see Instance Graph on page 57.
Instance graph
The Instance graph panel is used to present a graph of the
selected instance, and its related instances and roots. For more
information about the instance graph, see Instance Graph on
page 57.
Referenced by
The Referenced by panel is only available for managed type
instances and shows a tree containing all the instances, static
fields, and other roots that references the selected instance, also
called referrers in this manual. The details of an instance
referencing the selected instance can be viewed by double-clicking
the corresponding entry in this list or by selecting Show
Instance Details from the menu. The items can be sorted by
clicking on the column headers. The following information is
presented for each entry in this list:
Type
This column shows the type name of the instance or
type that references the selected instance. If the
reference is an unidentified root, this column will
contain the text: <root>.
Instance/Field/Method
This column shows different information depending on
the type of the referrer:
If the referrer is an instance, this column shows the
instance number of that instance, followed by the
name of the field (or item in an array) containing
the reference will be appended at the end (e.g.,
#89,123.m_referenceField or #12,890[5]).
If the referrer is a static field root, this column
shows the name of the static field
If the referrer is a local variable or method
argument root referrer, the name of the method is
presented in this column.
If the referrer is a <GCHandle>, this column will
present the instance number of the <GCHandle>,
References/Wraps
The References/Wraps tree is only available for managed type
instances and shows a tree of all references from the selected
instance, i.e., the values of all non-null reference fields. If the
instance wraps any resource instance, the tree also contains all
resource instances that are wrapped by the selected instance. For
more information, see Wrapped by on page 134. If the instance
does not wrap any resource instances (or the unmanaged resource
tracker is not enabled), the page name is only References.
The details of a reference can be viewed by double-clicking an
entry in this list. The list can be sorted by clicking on the column
headers. The following information is presented for each entry in
this list:
Field
This column shows the name of the field (or fields) that
contains the reference or the wrapped resource
identifier.
Type/Resource
This column shows the type name of the instance
referenced by the selected instance, or the resource
Field values
If instance data have been collected for the selected instance, the
field values are presented in the Field values tree. If instance data
Presentation Style
Using the Preferences settings, it is possible to select two different
styles of presenting the fields, i.e., Visual Studio and Flat.
Visual Studio
The Visual Studio style mimics the way fields are presented in the
Visual Studio Variables windows. The fields are presented in a
hierarchy based on the type they are declared in, and they are
sorted alphabetically.
Value Presentation
The way the field value is presented depends on the type of the
field:
Primitive numeric type (e.g., byte, short, int, long, float,
and double):
The value is converted to its string representation. A suffix
is added to floats and doubles (f and d, respectively).
Reference type:
If the reference field is null, then it is presented as null.
Otherwise, the instance number of the instance it
references is presented.
If the referenced instance is a string (System.String),
the contents of the string are presented after the instance
number.
If the referenced instance is a boxed primitive numeric
type, the value of the unboxed instance is presented, within
braces, after the instance number.
Otherwise, if the type of the referenced instance differs
from the type of the field, the name of the type is presented,
within braces, after the instance number. For example, if
the field type is System.Collection.ICollection and
the referenced instance is of type
System.Collection.ArrayList, the value will be
presented as: #xxxx {ArrayList}.
The + can be clicked to investigate the field values of the
referenced instance. To view the details of the referenced
instance, double-click the reference or select Show
Instance Details.
Value type:
A text representation of the fields in the value type is
presented in the Value column, within braces. Additional
information about the fields in the value type is available by
clicking +.
Wrapped by
The Wrapped by tree is only available for unmanaged resources
instances and shows a tree of all the managed instances that wrap
the selected resource instance. A managed instance is considered
to wrap a resource instance if it contains a field that is equal to the
identifier of the resource instance.
For more information about the information presented in this tree,
see Referenced by on page 129.
Call CreateWindowEx
Call CreateBitmap
CreateBitmap returns HBITMAP (#123)
CreateWindowEx returns HWND (#124)
In this scenario, the HWND #124 and HBITMAP #123 are related,
since the HBITMAP instance was created while CreateWindowEx
was still executing. The HBITMAP instance is a nested instance of
the HWND instance, and the HWND instance is an outer instance
of the HBITMAP instance.
The details of a related resource instance can be viewed by double-
clicking the corresponding entry in this list or by selecting Show
details from the menu. The list can be sorted by clicking on the
Root path
The Root paths panel is used to show the root paths of the
instance. A root path is the path of referrers from the selected
instance to a root. A root can, for instance, be a static field, a local
variable, or a method parameter. If a root cannot be identified, the
roots are simply referred to as <root>. For an unmanaged
resource instance, the wrapper instances are used as referrers to
the instance. If no wrapper instances exist, no root paths will be
presented.
The root path can be extremely useful for identifying memory
leaks. When using the observer pattern or caching instances, it is
very easy to forget to remove an observer or to let the cache grow
too much. The root path shows why an instance has not been
garbage collected.
The shortest path from the selected instance to each root is
presented, and the shortest path is shown first. The navigator
buttons above the root path can be used to select which root path
to show.
At the top of the Root path table is the nearest referrer. The
farther a referrer is from the selected instance, the farther that
referrer is down the list. At the bottom of the list is the root
referrer, which is either presented as the name of a static field, a
method, or as <root>.
The following data is presented for each referrer:
Namespace
This column shows the namespace name of the instance
or type that references the selected instance. If the
reference is an unidentified root, this column will
contain the text: <root>.
Name
Instance creation
The Allocation call stack/Call stack panel and the Function
panel provide information about how the instance was created.
The call stack panel presents the call stack of the instance
allocation or creation. The Function panel is only available for an
unmanaged resource instance. It presents information about the
arguments that were used when calling the creation function.
Methods View
The methods view is used to present information about methods
that have performed instance allocations.
The methods information is presented in a tree. This tree includes
three levels of information:
1. Container information
The assembly/module, type, and namespace that
contains the base method. This level presents summary
information from child items in the tree. For example,
the Live instances summary information for a type
includes the sum of all live instances created by all
methods in the type.
This level is only included if the Show hierarchical
checkbox is checked.
2. Base method
This level presents the base methods and provides
allocation information for the method itself. The actual
allocations included in the information depends on the
Include setting described below.
Collapse all
Collapses all expanded nodes in the call tree.
Memory View
The native memory is presented using a tree view. Each node of
the tree represents a type of memory. The information presented
for each node is the name of the memory type and the memory
used by that type. If the tree node has children, the memory
presented will be the sum of the memory of all the child nodes.
At the top level, the memory is categorized as: Private, Shared and
Potentially shared. (For more information about the categories, see
the section about Private, Shared, and Potentially Shared
Memory on page 151.)
Underneath this category, the memory is further divided into
different types depending on whether the memory data come from
the profiled process or not.
For the profiled process, the following information is presented:
Managed heaps
JIT
Code
Thread stacks
Identified resources
Unidentified unmanaged heaps
Managed heaps
The managed heaps are divided into the Generation #0 to
Generation #2 heaps, the large object heap and overhead.
Generation Heaps and Large Object Heap
The generation heaps and the large object heaps are further
divided into Data, Holes, and Unreachable.
Data represents memory that is actually used by an allocated
instance.
Holes represent memory that is unused between two allocated
instances. Holes appear when the heap is not fully compacted,
due to pinned instances or optimizations in the garbage collector.
Unreachable represents memory that is currently used by an
allocated instance, but the instance is not reachable from any root.
Unreachable instances may appear after a generation #0 or a
generation #1 collection, since not all instances are collected.
NOTE 2! Since the garbage collector has to look at all instances for
references to other instances, most instances will be mapped to
physical memory. Thus, the Physical memory view and Committed
memory view of the GC heap will be mostly the same. Large
unused instances (larger than the page size of 4096 bytes) that
contain no references may be swapped out to the page file in a low
memory condition (or if the Trim working set button is
pressed).
JIT
This node is only presented for the profiled process.
JIT memory is the memory used by the JIT-compiled code of the
process. The profiler tries to match the memory with the module
(.exe or .dll) that contains the code (or actually the IL code that
was JITted). If it succeeds, the memory is presented under the
Code node using the name of the matched module; otherwise the
memory is presented as <Other>.
If low-impact profiling is enabled, then JIT memory will give
information about how JITted code is shared between processes. If
low-impact profiling is not enabled, all JITted code will be private
to the profiled process, since the code has been modified by the
profiler.
Code
Code memory is executable memory that can be private, shared, or
potentially shared. The profiler tries to match the memory with the
module (.exe or .dll) that contains the code. If it succeeds, the
memory is presented under the Code node using the name of the
matched module; otherwise, the memory is presented as
<Other>.
Thread stacks
This node is only presented for the profiled process.
The Thread stacks node presents the memory used by the thread
stacks in the process. It is divided into Managed thread stacks,
which presents the memory used by threads managed by the .NET
runtime, and Unmanaged threads, which presents the
memory used by all other threads.
Identified resources
This node is only presented for the profiled process, and it is
presented only if the unmanaged resources tracker is enabled.
It presents the memory used by unmanaged resources tracked by
the unmanaged resources tracker. Only memory that is not
Other Data
This node is only presented for the profiled process.
Other data memory is memory that can be private, shared or
potentially shared. This memory can be, for instance, resources of
a module or memory allocated using VirtualAlloc.
The profiler tries to match the memory with the file (.exe, .dll or
memory mapped file) that contains the data. If it succeeds, the
memory is presented under the Other data node using the name of
the matched module, otherwise the memory is presented as
<Other>.
Data
This node is presented only for non-profiled processes. A profiled
process has more details on the memory types (for instance, it
knows if the memory is part of the GC heap), so it will use the
Other data node to present additional information about data
memory.
Data memory is memory that can be private, shared, or potentially
shared. This memory can be, for instance, resources of a module or
memory allocated using VirtualAlloc or HeapAlloc. The GC heap is
also a part of the Data memory.
The profiler tries to match the memory with the file (.exe, .dll or
memory mapped file) that contains the data. If it succeeds, the
memory is presented under the Data node using the name of the
matched module; otherwise the memory is presented as
<Other>.
Real-Time Page
The Real-time page presents real-time information about the
managed memory usage, native memory and resource usage, and
data from collected performance counters. It gives a good overview
of how the memory is being used during the life-time of the
profiled process.
The information includes, for example, the number of total
instances, total bytes, live instances, and live bytes. It is updated at
least once every second. The history of all collected data is stored
as series of data and can be used to present the data graphically.
Note that the numbers presented are not dependent on the
snapshots selected; if the session time selector is active, they
reflect the memory activity that had taken place at the selected
time, otherwise they reflect the memory usage at the end of the
session. For more information about the session time selector, see
Session Time Selector on page 168.
Three panes are used to present the real-time information, define
the data to present, and to define presentation settings: the
Graph, Settings and series, and Types/Resources panes.
The colored legend in the Settings and series pane indicates the
color and type of the graph when the series is included in the graph
view. For more information on working with the graph, see
Working with the Graph on page 165. For more information
about the Settings and statistics pane, see Settings and Series
on page 154.
The next graph shows the plots when the conditions are not
optimal. After each generation #0 collection, the total bytes have
increased considerably, even though the live bytes are almost
constant. Not even a generation #1 collection manages to decrease
the total bytes on the heap. Only after a full generation #2
collection, does the number of total bytes become equal to the
number of live bytes.
Real-time layout
The real-time layout drop-down list is used to select the layout to
use when presenting real-time data. The layout defines which data
sources to use (i.e. session files), presentation settings, which data
series to include, and how data should be presented in the real-
time graph. A single layout (Standard) is included with the
installation of .NET Memory Profiler. This layout includes the
most important real-time information for the .NET runtime and
native resources (if native resource tracking is enabled). Additional
real-time series can be added using the Add and Add series
links.
The Save/Manage button is used to show a drop-down dialog
where real-time layouts can be managed.
Sources
The Sources group is used to define the sources used for the real-
time data. By default, the current session is used as the real-time
source, but it is possible to add external session files as additional
sources.
Enterprise only An additional source is added using the Add link on the right
hands side of Sources. The will show the Add/Modify source
drop-down dialog.
Session
Under Session the session to use as source is selected. It can be
the current profiler session, or an external session file (selected
using the Browse button).
Settings
The Settings group is used to define settings for the real-time
graphs and real-time series values. It includes the following
settings:
Averaging length
Defines the length of the averaging window, in seconds. This
setting is used for series where the value is calculated as moving
average.
When Moving average is selected as the value for a real-time series
(and graph), an averaging window is used to calculate the value. If
the averaging window is short (e.g. one second), this can cause the
graph to be a bit noisy, as can be seen in the screenshot below.
The Averaging length slider can be used to increase the size of the
averaging window. This will reduce the noise in the graph and can
make the graph easier to interpret.
Real-time series
Below the graph settings, the included real-time series are
presented, grouped by the series category. Currently the following
categories are available:
Types
This category includes memory usage information for
specific types. To make a type available as a real-time
series and to include real-time type information in the
graph, it must be marked for real-time collection using
the Types/Resources real-time view. The real-time data
available includes allocation and instances information
for the type. For more information about the
information presented, see Managed memory
statistics on page 162.
Resources
This category includes resource usage information for
specific resources. To make a resource available as a
real-time series and to include real-time resource
information in the graph, it must be marked for real-
time collection using the real-time Types/Resources
view. The real-time data available includes allocation
and instances information for the resource. For more
information, see Native resources statistics on page
163.
New series are added using the Add link (in the series category
header), or by using the Add series link below the series
categories. The Add link is used to an additional series in the
same category. The Add series link can be used to add series to
a new category.
Total instances
This is the total number of instances in the managed
heap, which includes both instances that are reachable
and instances that cannot be reached.
Live instances*
This is the number of live instances at the last GC.
Total bytes
This number shows the total number of bytes used by
all the instances in the managed heap, including both
instances that are reachable and instances that cannot
be reached.
Live bytes*
This number shows the number of bytes used by the
instances that were reachable at the last GC.
Allocs/sec
This number shows the number of allocations
performed per second. It can be presented as a moving
average, or be accumulated, either over a time interval
or since a specified start time.
Bytes/sec
This number shows the number of bytes allocated per
second. It can be presented as a moving average, or be
accumulated, either over a time interval or since a
specified start time.
Real-time Types/Resources
The axis overview bar gives you an overview of the collected real-
time data and allows you to manually select the x-axis range of the
graph. Below is an explanation of the different parts:
Left shaft
Data Reader
The data reader can be used to read the value at a specific location
of a plot. The data reader is enabled when the time selector is
disabled. Simply move the mouse over a plot to read the value of
the plot. When the cursor is close enough to a plot, a tooltip
window will appear, showing the name of the plot and the X-
(time) and Y-value of the plot at the location of the cursor.
The time selector is used to select the time for which the real-time
information should be presented. It is enabled using the Show
time selector command. When the time selector is enabled, the
mouse can be moved over the graph to select the time for the real-
time data. The numbers in the statistics view and the
Types/Resources table will be updated to reflect the selected time.
There can be a slight delay until the updated values have been
retrieved, especially when remote profiling. During this delay, the
old numbers will be presented in a gray color.
It is possible to lock the time selector at a specific time by left-
clicking in the graph. This makes it possible to move the mouse
away from the graph, for instance to select a type or resource in the
Types/Resources view. The time selector will be unlocked if the
mouse is moved into the graph again, or by left clicking in the
graph again.
NOTE! If the time selector is moved to the left of the first data
point, then no values will be shown.
Dispose Tracker
The dispose tracker is a powerful tool for tracking memory
problems pertaining to disposable types. Any type that implements
IDisposable signals that all its instances should be disposed as
soon as they are not needed anymore. Often these types wrap
unmanaged resources, such as file handles, database connections,
bitmaps, etc. The types may also be disposable because they
contain references to other disposable instances, or they simply
need to know when they are not needed anymore.
Disposed Instances
Usually, the number of disposed instances for any type should be
zero or close to zero. If it is not zero, it may indicate a memory
GC.SuppressFinalize( this );
}
void IDisposable.Dispose()
{
Close();
}
}
Undisposed instances
If all instances of a class are properly disposed, the number of
undisposed instances will be zero. As mentioned previously, it is
preferable to make sure that all instances are properly disposed,
but there is a set of disposable classes whose instances do not need
to be disposed. These classes do not contain unmanaged resources,
do not reference other disposable classes, do not need to be
cleaned up, and do not have a finalizer. Usually these classes
should not be disposable in the first place, but they might be
derived from a base class that expects its derived classes to
possibly contain unmanaged resources, or they might be used as
base classes for other classes that may contain unmanaged
resources.
An example of a disposable class that does not need to be disposed
is the System.IO.MemoryStream class, since it only references
managed memory and has no finalizer. It is derived from
System.IO.Stream, an abstract class that can expect classes
derived from it to contain unmanaged resources, e.g., file handles
and network sockets. It therefore implements IDisposable, even
though not all derived classes will contain unmanaged resources,
e.g., System.IO.MemoryStream.
Even though there are disposable classes whose instances do not
need to be disposed, it is recommended that all instances of all
disposable classes always be disposed, unless you are certain that
an instance does not need to be disposed.
Example:
Assume that 1000 instances have been allocated since the
comparison snapshot and that the selected snapshot was
collected using a generation #0 garbage collection 100
seconds later.
900 instances were garbage collected before the
selected snapshot
o 700 were garbage collected by the first
generation #0 GC
o 100 were garbage collected by the first
generation #1 GC
o 100 were garbage collected by a generation
#2 GC
25 instances are live in generation #1 at the time
of the selected snapshot
75 instance are live in generation #2 at the time
of the selected snapshot
If we sum this up, the distribution of the 1000 instance
allocations is as follows:
Generation #0: 700 instances
Unreachable instances
If a generation #0 heap snapshot has been performed, it is possible
that there are instances left on the heap that are not reachable
from any root. The reason for this is that the garbage collector does
not make a full reachability analysis when performing a generation
#0 collection and does not know whether some instances are
reachable or not.
The number of unreachable instances of each type is presented
when showing heap utilization information. This number can be
used to analyze how well the garbage collected heap is utilized.
Creation Context
When an unmanaged resource instance is created, the call stack
and the instance are associated with a creation context. The
creation context indicates how the resource instance was created.
Below is a presentation of the different creation contexts that exist:
Managed runtime
The "managed runtime" creation context indicates that
the instance was created by the runtime itself, e.g.,
internal structures used when loading classes and
JITting.
Unmanaged interop
Other unmanaged
The "other unmanaged" creation context indicates that
the instance was created by a thread that has not run
any managed code.
AppDomains Tracker
The AppDomains tracker can be used to associate instances with
the AppDomain they were created in. The AppDomains tracker is
particularly useful when profiling ASP.NET applications, or other
applications that rely on AppDomains for separation.
The AppDomains tracker is enabled by using the Enable
AppDomains tracker check box General page under the
session settings or in the start profiling wizard.
If the AppDomain tracker is enabled, the AppDomain is recorded
for each instance allocation (both managed instances and
unmanaged resource instances). When comparing snapshots that
contain AppDomain information, an AppDomains selector will be
visible in the profiler window. This selector can be used to filter the
snapshot data so that it only includes information about instances
allocated in the selected AppDomain.
GC Handle Identification
GC handles are used to create handles to managed instances. They
act as roots for the garbage collector and can be used to prevent an
instance from being collected, to pin a managed instance in
memory, or to create a weak reference to an instance. The .NET
Framework provides access to GC handles through the
System.WeakReference class and the
System.InteropServices.GCHandle structure. The runtime
also uses GC handles internally to keep track of certain instances,
such as managed COM interfaces and static field data.
.NET Memory Profiler can identify the internal GC handles that
are created by the runtime itself or by using the WeakReference
and GCHandle types. If GC handle identification is enabled (using
Preferences in the Options dialog), a pseudo-Type called
<GCHandle> will be presented by the profiler. This type
represents the internal GC handles used by the runtime; it does
not map to a real managed type. The <GCHandle> type and its
instances can be investigated in the same way as any other
managed type, e.g., by viewing the details of the type or the
instances.
Root Kind
A GC handle acts as a root for the garbage collector, and,
depending on the kind of root, it will affect the garbage collector in
NOTE! This can considerably affect the way the root paths are
presented for some types and instances. For example, a top level
System.Windows.Forms.Form is kept alive using a <GCHandle>
allocated using GCHandle.Alloc. If GCHandle roots are treated
If GCHandle roots are not treated as identified, the direct root will
not be included, as can be seen in the picture below.
Debug Profiling
Professional and It is also possible to start a profiling session that runs under the
Enterprise only Visual Studio debugger. To start a debug profiling session,
select the Debug with Memory Profiler command under
Profiler menu. This will start the profiling session similar to the
way it is started using the Start Memory Profiler command.
When debug profiling is enabled, the instance number for each
instance is presented in all watch windows (Watch, Locals , Auto,
and debugger tool tips), making it easier to identify instances while
debugging.
Starting NmpCore
NmpCore can be started from a command line, using the
command:
NmpCore [<options>] <process>
To start NmpCore, the command line must at least specify the
process to profile or specify that NmpCore should run as a remote
agent. The process to profile can be specified using the following
command line arguments:
A similar command line can also be used even when starting the
program from Nmpore. Note that the /p argument must come last.
NmpCore /cs3 /ac5 /sf "C:\Sessions\DumpSession.prfsession" /p <path>\SomeProcess.exe
This target will start profiling a process based in the settings in the
provided profiler project.
The NmpCore NuGet package includes a target that will run
NmpCore on all files in the NmpCore item group. A profiler project
can be added to the NmpCore items by selecting the build action
NmpCore. The target forwards the following MSBuild properties to
NmpCore:
TargetPath, TargetDir, TargetName, TargetFileName
The .NET Memory Profiler API can be used to control the profiler
within the profiled process, retrieve the memory usage (allocations
and instances), and to detect potential memory leaks using
assertions. This API is very well suited for doing automated testing
of memory usage errors, e.g., by using a unit testing framework
such as NUnit, XUnit, or VSTest.
The API can be accessed by adding the SciTech.MemProfilerApi
NuGet package. This package includes the class library
SciTech.MemProfilerApi.dll, which supports .NET
Framework 2.0 or later, and .NET Standard 1.1 or later. The class
library is also available under the Redist folder in the .NET
Memory Profiler installation folder (default location: C:\Program
files\SciTech\NetMemProfiler5\Redist).
This class library contains the two main classes of the API:
SciTech.NetMemProfiler.MemProfiler
//
MethodThatShouldBeTested();
Assert.LessOrEqual(1, instances.NewInstancesCount);
Assert.LessOrEqual(10, allocs.AllocatedInstancesCount);
Creating a dialog, showing it, and then disposing it, are operations
that should not create any new live instances. In reality, however,
quite a few new live instances might be created as a side effect of
creating the dialog. The first time the method is executed the
SomeDialog type might get loaded, creating a new live Type
instance (actually a RuntimeType), and new strings (e.g., the
name of the type).
In this case, it is known that after the dialog has been shown and
disposed, it should be eligible for garbage collection. If the
SomeDialog instance cannot be garbage collected, then we might
have something referencing the instance unintentionally (for
instance, a left-over event handler).
To assert that the SomeDialog instance can be garbage collected,
the NoNewInstances method can be used:
MemAssertion.NoNewInstances( typeof( SomeDialog ) );
This method will make a full garbage collect and then assert that
no new instances of the SomeDialog class exist on the GC heap.
The ShowDialog code with the memory assertion looks like this:
void ShowDialog()
{
// Establish a base snapshot
MemProfiler.FastSnapshot();
void DoShowDialog()
{
using( SomeDialog dlg = new SomeDialog() )
{
dlg.ShowDialog();
}
}
DoShowDialog();
// Assert that no new instances of SomeDialog has been
// created. The FastSnapshot collected at the
// beginning of the method will be used as
// reference.
MemAssertion.NoNewInstances( typeof( SomeDialog ) );
}
The problem with the assertion above is that at the time of the
assertion call, a new Type array exists, and the assertion will fail.
If BeginAssertions/EndAssertions is used, this will be
avoided, since instances created in-between BeginAssertions
and EndAssertions are ignored by the memory assertion.
MemProfiler.FastSnapshot();
BeginAssertions();
MemAssertion.NoNewInstancesExcept( new Type[] { typeof( SomeClass),
typeof( SomeOtherClass ) );
EndAssertions();
/// <summary>
/// This method opens up a form that allows the user
/// select a bitmap. The selected bitmap is loaded
/// and made available through the LoadedBitmap
/// property.
/// </summary>
Bitmap LoadBitmap()
{
using( LoadBitmapDialog dlg = new LoadBitmapDialog() )
{
dlg.ShowDialog();
return dlg.LoadedBitmap;
}
}
/// <summary>
/// Tests the memory usage of the LoadBitmap method.
/// </summary>
Bitmap TestLoadBitmap()
{
// Establish a base snapshot
MemProfiler.FastSnapshot();
return loadedBitmap;
}
In this case, it is not clear what happens if there are, for instance,
10 new instances of DerivedClass2. It is allowed by the
AllowInstances call, but since 10 new instances exist, the
MaxNewInstances should fail. As mentioned previously, the
AllowXXX and MaxXXX assertions take precedence over the
NoXXX assertions, but the precedence between AllowXXX and
MaxXXX is not defined.
To resolve this ambiguity, DerivedClass2 should not be included in
the AllowNewInstances call. This can be accomplished by using
a TypeSet. The TypeSet class is an immutable class that is used
to define a set of Types by adding or removing types from another
TypeSet. Two predefined TypeSets are available, TypeSet.Empty
(containing no types) and TypeSet.All (containing all loaded
types).
The Add and Subtract methods can be used to create a new
TypeSet with the specified types added or removed. For example,
the following code can be used to create a TypeSet that contains
the BaseClass type and all types derived from it, except the
DerivedClass2 type.
// Start with an empty TypeSet;
TypeSet ts = TypeSet.Empty;
// Add DerivedClass1 and all its derived classes to a
// new TypeSet
ts = ts.Add( typeof( DerivedClass1 ), true );
// Remove DerivedClass2
ts = ts.Subtract( typeof( DerivedClass2 ) );
TypeSet ts = TypeSet.Empty;
ts = ts.Add( typeof( DerivedClass1 ), true );
ts = ts.Subtract( typeof( DerivedClass2 ) );
For more information about the TypeSet class, see the .NET
Memory Profiler API Reference documentation.
[NoNewInstances(System.Windows.Forms.*,
IncludeSubclasses=true)]
[NoNewInstances(System.Drawing.*,IncludeSubclasses=true)]
[MaxNewInstances( typeof( Bitmap), 1]
Bitmap LoadBitmap()
{
using( LoadBitmapDialog dlg = new LoadBitmapDialog() )
{
dlg.ShowDialog();
return dlg.LoadedBitmap;
}
}
When running this code under .NET Memory Profiler, the profiler
will replace this method with a method looking similar to this:
using System.Drawing;
using SciTech.NetMemProfiler;
Bitmap LoadBitmap()
{
// --- Generated by .NET Memory Profiler
// Collect a reference snapshot
MemSnapShot refSnapshot = MemProfiler.FastSnapshot();
try
{
// --- Original code
using( LoadBitmapDialog dlg = new LoadBitmapDialog() )
{
dlg.ShowDialog();
return dlg.LoadedBitmap;
}
// --- Generated by .NET Memory Profiler
}
finally
{
// Build the AssertionsDefinition, as
// defined by the attributes.
using( MemAssertion.BeginAssertions() )
{
AssertionsDefinition ad = new AssertionsDefinition();
ad.NoNewInstances( System.Windows.Forms.*, true );
ad.NoNewInstances( System. Drawing.*, true );
ad.MaxNewInstances( typeof( Bitmap ), 1 );
MemAssertion.Assert( refSnapshot, ad );
}
}
}
[TestFixtureTearDown]
public void FixtureTearDown()
{
MemAssertion.AssertionFailed -= new EventHandler(
MemAssertion_AssertionFailed );
}
void MemAssertion_AssertionFailed(
object sender, EventArgs e )
{
// A memory assertion has failed. Inform the
// unit testing framework
Assert.Fail( Memory assertion failed );
}
[Test]
[NoNewInstances( typeof( Control ),
IncludeSubclasses=true )]
public void SomeTest()
{
// ...
}
}
MemAssertion.NoNewInstances( ... );
}
#if ENABLE_MEM_ASSERTIONS
MemAssertion.NoNewInstances( ... );
#endif
}
ShowDialog();
Generic Types
Only classes that have had at least one instance allocated are
presented by the profiler, and an instance of a generic class can
only be allocated when all generic type parameters are known.
Therefore, the generic classes presented by the profiler are always
fully instantiated (i.e., all type parameters are known).
The names of generic classes are presented using C# syntax. The
type parameters are presented using the name of the type unless
the type is a primitive type (e.g., int, float, and string). Primitive
type parameters are presented using the C# name of the type. For
example, the name of a
System.Collections.Generics.Dictionary<K,V> class
Generic Methods
Generic methods are defined in the .NET documentation as
methods that are declared with type parameters (e.g., Swap<T>(
T x, T y ) ). Here, methods that access class-level type
parameters are also considered to be generic methods. For
instance, the DoSomething method below is considered a generic
method.
class GenericClass<T>
{
void DoSomething( T value );
}
Known Issues
Below is a list of known issues with the profiler.
memory leak
A memory leak is characterized by an unintentionally increasing
amount of memory usage. Traditionally a memory leak occurs
when memory is allocated and the reference is discarded before
the memory is released. In a garbage collected environment this
can never occur, since an instance is released as soon as all
references to it has been removed. On the other hand, it is possible
to unintentionally reference an instance by a long-term reference,
by for instance adding an observer to a long-living instance and
never removing it. The behavior of having unintentional long-term
references to instances is similar to the traditional memory leak
and will be referred to as a memory leak in this manual.
code injection
Code injection is the process of adding additional code to a
method, or adding completely new methods. By injecting code it is
possible to get information about when certain things happen, e.g.
when an instance is disposed.
live instance
A live instance is an instance that is reachable by walking the
references from any root.
disposable
A disposable class is a class which implements the
System.IDisposable interface, or is derived from a class
implementing System.IDisposable. A disposable instance is an
instance of a disposable class.
instance number
Instance number is a unique number identifying a specific
instance. Whenever an instance is part of a heap snapshot it is
referrer
A referrer is an instance field, static field, local variable or method
argument which references an instance.
root reference
Root references acts as starting points when the garbage collector
performs a heap walk. Root references can for instance be local
variables, method arguments, references to static reference fields
or other internal references in the common language runtime.
Index D
Data 150
Debug Profiling 183
Declarative Memory Assertions 207
Details (allocated instances) 145
Disposable Types Classification and
Undisposed Instances 51
Dispose Tracker 169
. Disposed Instances 170
.NET Memory Profiler API 196 Duplicate Instances Additional Bytes 48
Duplicate Instances Detection 47
A
E
A Comment on the Appearance of Total and
Live Plots 152 Exit Codes 31
Access Profiler Settings 185
Adding and Removing Sessions 80 F
Adding Comments to the Real-time Graph Fast Column Filter 94
211 Field Sets 92
Allocation Stacks 123 Field values 131
Analysis Issues List 39 Find Memory Leaks 12
AppDomains Tracker 176 Find out Information about the Native
Attach to a Process 20 Memory of a .NET Process 15
Attach to Process using Profiling API 214
Automatic Memory Analysis 31 G
Available Command Line Arguments 27
GC Handle Identification 178
C General Settings Page 102
Generic Methods 215
Call Stack Reducer 177 Generic Types 215
Call Stacks Functions 141 Graph 152
Call Stacks Reduction 176 Graph Presentation 57
Call Stacks Settings Page 111 Graph Synchronization 67
Call Stacks View 142 Guided Profiling and the Tasks Window 70
Call Stacks/Methods Page 136
Code 149 H
Column Layout and Customization 95
Combined Instances Graph 63 Handling Failed Memory Assertions 212
Command Line Arguments 26 Heap Utilization Tracker 172
Command Line Examples 193 Held and Reachable Instances 53
Commands 86 Held by a Set of Instances 56
Commands in Visual Studio 185 Held Duplicates 49
Compare Snapshots using the Profiler How to Call the .NET Memory Profiler API
Projects Explorer 24 Methods 210
Comparing Project Snapshots 80 How to... 2
T
Tasks Window 72
The .NET Memory Profiler Window 83
The NmpCore Tool 191
Thread stacks 149
Type/Resource/Filter Details Page 119
U
Undisposed instances 172
Unidentified unmanaged heaps 150
Unmanaged Resources Tracker 174
Unreachable instances 174
Use Assertions to Detect Memory Leaks 199
Use the Memory Analyser 11