How To Find Memory Leaks In Net Application
Anyone who worked on a large enterprise project knows memory leaks are like rats in a big hotel. You might not notice when there are few of them, just y'all always have to be on guard in case they overpopulate, break into the kitchen, and poop on everything.
Finding, Fixing and learning to Avert Memory Leaks is an important skill. I'll list 8 best practice techniques used past me and senior .Internet developers that advised me for this commodity. These techniques will teach you lot to find when at that place's a memory leak trouble in the application, to observe the specific memory leak and to fix it. Finally, I'll include strategies to monitor and report on memory leaks for a deployed program.
Defining Memory Leaks in .Net
In a garbage collected surround, the term memory leaks is a scrap
There are 2 related core causes for this. The kickoff core crusade is when you take objects that are still referenced but are effectually unused. Since they are referenced, the garbage collector won't collect them and they volition remain forever, taking up memory. This tin can happen, for example, when you register to events but never unregister.
The 2nd crusade is when you somehow allocate unmanaged memory (without garbage collection) and don't free it. This is not so hard to do. .NET itself has a lot of classes that allocate unmanaged retentiveness. Almost anything that involves streams, graphics, the file organisation or network calls does that nether the hood. Commonly, these classes implement aDispose method, which frees the memory (nosotros'll talk about that later). Yous can hands allocate unmanaged retention yourself with special .NET classes (similar Marshal) or PInvoke (at that place's an example of this further on).
Let's motility on to my best practice techniques list:
If you lot go to Debug | Windows | Prove Diagnostic Tools, you'll come across this window. If you lot're like me, y'all probably saw this tool window after installing Visual Studio, closed it immediately, and never idea of it again. The Diagnostic Tools Window tin can be quite useful though. It can easily assist y'all detect 2 problems: Retention Leaks and GC Force per unit area.
When you have Retentiveness Leaks, the Process Retentiveness graph looks like this:

Yous can run across with the yellow lines coming from the tiptop that the GC is trying to complimentary retentiveness, but it nonetheless keeps rising.
When you accept GC Pressure, the Procedure Retention graph looks similar this:

GC Pressure is when you are creating new objects and disposing of them too quickly for the garbage collector to keep up. As you see in the picture show, the retention is close to its limit and the GC bursts are very frequent.
You won't be able to find specific memory leaks this way, only you tin detect that you have a retentivity leak problem, which is useful by itself. In Enterprise Visual Studio, the Diagnostics Window also includes a congenital-in memory profiler, which does allow to notice the specific leak. We'll talk near memory profiling in best practice #iii.
2. Detect Retentiveness Leak problems with the Chore Manager, Process Explorer or PerfMon
The second easiest style to detect major memory leak problems is with the Task Director or Process Explorer (from SysInternals). These tools can bear witness the amount of memory your procedure uses. If it consistently increases over time, you lot probably accept a memory leak.

PerfMon is a chip harder to use but can show a nice graph of your memory usage over time. Here'southward a graph of my awarding that endlessly allocates memory without freeing it. I'm using the Process | Private Bytes counter.

Notation that this method is notoriously unreliable. You might have an increase in memory usage just because the GC didn't collect information technology yet. At that place'south likewise the matter of shared memory and private retentivity, then you can both miss memory leaks and/or diagnose retentivity leaks that aren't your own (explanation). Finally, you might error retentivity leaks for GC Pressure. In this example, you don't have a memory leak just you lot create and dispose of objects so fast that the GC doesn't keep up.
Despite the disadvantages, I mention this technique because information technology's both easy to utilise and sometimes your only tool. Information technology's also a decent indicator something is wrong when observing for a very long flow of fourth dimension.
3. Use a memory profiler to find retentiveness leaks
A memory profiler is like the Chef's Knife of treatment memory leaks. It'south the main tool to discover and fix them. While other techniques can be easier to use or cheaper (profiler licenses are costly), information technology'southward best to be skillful past with at least one memory profiler to finer solve retentivity leak problems.
The big names in .NET memory profilers
All memory profilers work in a similar mode. You can either attach to a running process or open a Dump file. The profiler volition create a Snapshot of your process' current retentiveness heap. You tin can analyze the snapshot in all kinds of ways For example, here's a list of all the allocated objects in the current snapshot:

You can see how many instances of each type are allocated, how much memory they take, and the reference path to a GC Root.
A GC Root is an object which the GC can't costless, so anything that the GC root references also tin't exist freed. Static objects and the local objects in the current agile Threads are GC Roots. Read more in Understanding Garbage Collection in .Net.
The quickest and most useful profiling technique is to compare ii snapshots where the retention should return to the aforementioned state. The get-go snapshot is taken earlier an operation, and another snapshot is taken after the operation. The exact steps are:
- Beginning with some kind of Idle state in your application. This could be the Main Menu or something similar.
- Take a snapshot with the Memory profiler by attaching-to-process or saving a Dump.
- Run an operation where you lot suspect a memory leak is created. Return to the Idle state at the terminate of it.
- Take a second snapshot.
- Compare both snapshots with your memory profiler.
- Investigate the New-Created-Instances, they are probably memory leaks. Examine the "path to GC Root" and attempt to understand why those objects weren't freed.
Here's a keen video where 2 snapshots are compared in SciTech retentiveness profiler and the memory leak is found:
4. Utilise "Make Object ID" to find memory leaks
In my last article 5 Techniques to avoid Retentivity Leaks by Events in C# .Cyberspace you lot should know I showed a technique to find a memory leak by placing a breakpoint in the grade Finalizer. I'll testify yous a similar method here that's even easier to use and doesn't require code changes. This one utilizes the Debugger's Make Object ID feature and the Immediate Window.
Suppose y'all suspect a certain course has a memory leak. In other words, you suspect that after running a certain scenario, this class stays referenced and never collected past the GC. To observe out if the GC actually collected it, follow these steps:
- Place a breakpoint where the instance of the class is created.
- Hover over the variable to open the debugger's data-tip, then right-click and use Make Object ID. You can type in the Immediate Window $1 to see that the Object ID was created correctly.
- Terminate the scenario that was supposed to free your example from references.
- Force GC drove with the known magic lines
GC . Collect ( ) ; GC . WaitForPendingFinalizers ( ) ; GC . Collect ( ) ; |
v. Blazon $1 again in the immediate window. If it returns null, and then the GC collected your object. If not, you have a memory leak.
Here's me debugging a scenario that has a memory leak:
And here's me debugging a similar scenario that doesn't accept a memory leak:
You can force garbage collection in the end by typing the magic lines in the immediate window, making this technique a fully debugging experience, with no need to change code.
Of import: This exercise doesn't work well in .Cyberspace Core 2.Ten debugger (issue). Forcing garbage collection in the same telescopic as the object allotment doesn't free that object. You can do information technology with a little more than effort by forcing garbage collection in another method out of scope.
5. Beware of mutual retention leak sources
There's always a risk of causing a memory leak, merely there are certain patterns that are much more than likely to practise so. I suggest to be extra careful when using these, and proactively cheque for retention leaks with techniques like the terminal best practice.
Here are some of the more than mutual offenders:
- Events in .Internet are notorious for causing memory leaks. You can innocently subscribe to an event, causing a damaging memory leak without even suspecting. This subject is and then of import that I dedicated an entire commodity to it: 5 Techniques to avert Memory Leaks by Events in C# .Internet yous should know
- Static variables, collections, and static events in-item should ever await suspicious. Call up that all static variables are GC Roots, so they are never collected by the GC.
- Caching functionality – Any type of caching mechanism can easily cause memory leaks. By storing cache information in-memory, eventually, it will make full and cause an OutOfMemory exception. The solution tin can exist to periodically delete older caching or limit your caching amount.
- WPF Bindings tin be dangerous. The rule of thumb is to always bind to a DependencyObject or to
a INotifyPropertyChanged object. When you fail to do so, WPF will create a potent reference to your binding source (meaning the ViewModel) from a static variable, causing a retentiveness leak. More information on WPF Binding leaks in this helpful StackOverflow thread - Captured members – It might be articulate that an Event Handler Method means an object is referenced, only when a variable is Captured in an anonymous method, then it'south as well referenced. Hither's an case of a memory leak:
public class MyClass { private int _wiFiChangesCounter = 0 ; public MyClass ( WiFiManager wiFiManager ) { wiFiManager . WiFiSignalChanged += ( s , e ) = > _wiFiChangesCounter ++ ; } |
- Threads that never terminate – The Alive Stack of each of your threads is considered a GC Root. This means that until a thread terminates, any references from its variables on the Stack volition not be collected by the GC. This includes Timers equally well. If your Timer'southward Tick Handler is a method, then the method'southward object is considered referenced and will not be nerveless. Here'southward an case of a retention leak:
public grade MyClass { public MyClass ( WiFiManager wiFiManager ) { Timer timer = new Timer ( HandleTick ) ; timer . Change ( TimeSpan . FromSeconds ( 5 ) , TimeSpan . FromSeconds ( 5 ) ) ; } private void HandleTick ( object country ) { // do something } |
For more on this bailiwick, check out my commodity 8 Ways You can Crusade Retentiveness Leaks in .NET.
6. Use the Dispose design to preclude unmanaged memory leaks
Your .NET application constantly uses unmanaged resources. The .Cyberspace framework itself relies heavily on unmanaged code for internal operations, optimization, and Win32 API. Anytime you lot use Streams, Graphics, or Files
.Cyberspace framework classes that utilize unmanaged code normally implement IDisposable. That's because unmanaged resources need to exist
public void Foo ( ) { using ( var stream = new FileStream ( @ "C:\Temp\SomeFile.txt" , FileMode . OpenOrCreate ) ) { // practice stuff } // stream.Dispose() will be called even if an exception occurs |
The using statement transforms the code into a attempt / finally argument behind the scenes, where the Dispose method is chosen in the
Just, fifty-fifty if you lot don't call the Dispose method, those resource will be freed because .NET classes use the Dispose Pattern. This basically ways that if Dispose wasn't called before, it's called from the Finalizer when the object is garbage collected. That is, if yous don't have a retentivity leak and the Finalizer really is called.
When you're allocating unmanaged resources yourself, then you definitely should use the Dispose pattern. Here's an example:
ane 2 3 iv 5 6 vii 8 9 x 11 12 xiii fourteen xv xvi 17 18 19 20 21 22 23 24 25 26 27 28 29 thirty 31 32 33 34 35 36 37 | public class MyClass : IDisposable { private IntPtr _bufferPtr ; public int BUFFER_SIZE = 1024 * 1024 ; // 1 MB individual bool _disposed = false ; public MyClass ( ) { _bufferPtr = Marshal . AllocHGlobal ( BUFFER_SIZE ) ; } protected virtual void Dispose ( bool disposing ) { if ( _disposed ) return ; if ( disposing ) { // Free any other managed objects hither. } // Free whatsoever unmanaged objects here. Marshal . FreeHGlobal ( _bufferPtr ) ; _disposed = truthful ; } public void Dispose ( ) { Dispose ( truthful ) ; GC . SuppressFinalize ( this ) ; } ~ MyClass ( ) { Dispose ( false ) ; } } |
The point of this pattern is to permit explicit disposal of resources. But besides to add a safeguard that your resources volition be disposed during garbage collection (in the Finalizer) if the Dispose() wasn't chosen.
The GC.SuppressFinalize(this) is besides of import. It makes sure the Finalizer isn't chosen on garbage collection if the object was already
7. Add together Memory Telemetry from Code
Sometimes, you might want to periodically log your retentiveness usage. Perchance you suspect your production Server has a memory leak. Peradventure yous desire to take some action when your memory reaches a certain limit. Or perchance you're only in the adept habit of monitoring your memory.
There's a lot of data we can get from the app itself. Getting electric current memory in-use is equally simple equally:
Process currentProc = Process . GetCurrentProcess ( ) ; var bytesInUse = currentProc . PrivateMemorySize64 ; |
For more information, you can use the PerformanceCounter grade that's used for PerfMon:
PerformanceCounter ctr1 = new PerformanceCounter ( "Procedure" , "Private Bytes" , Procedure . GetCurrentProcess ( ) . ProcessName ) ; PerformanceCounter ctr2 = new PerformanceCounter ( ".NET CLR Retentivity" , "# Gen 0 Collections" , Process . GetCurrentProcess ( ) . ProcessName ) ; PerformanceCounter ctr3 = new PerformanceCounter ( ".NET CLR Memory" , "# Gen one Collections" , Process . GetCurrentProcess ( ) . ProcessName ) ; PerformanceCounter ctr4 = new PerformanceCounter ( ".Internet CLR Memory" , "# Gen 2 Collections" , Procedure . GetCurrentProcess ( ) . ProcessName ) ; PerformanceCounter ctr5 = new PerformanceCounter ( ".Internet CLR Retentivity" , "Gen 0 heap size" , Process . GetCurrentProcess ( ) . ProcessName ) ; //... Debug . WriteLine ( "ctr1 = " + ctr1 . NextValue ( ) ) ; Debug . WriteLine ( "ctr2 = " + ctr2 . NextValue ( ) ) ; Debug . WriteLine ( "ctr3 = " + ctr3 . NextValue ( ) ) ; Debug . WriteLine ( "ctr4 = " + ctr4 . NextValue ( ) ) ; Debug . WriteLine ( "ctr5 = " + ctr5 . NextValue ( ) ) ; |
Information from any perfMon counter is bachelor, which is plenty.
You can get even deeper though. CLR MD (Microsoft.Diagnostics.Runtime) allows y'all to inspect your electric current memory heap and become any possible information. For example, yous can impress all the allocated types in memory, including instance counts, paths to roots and then on. You pretty much got a memory profiler from code.
To become a whiff of what y'all can achieve with CLR Physician, cheque out Dudi Keleti'southward DumpMiner.
All this information tin can be logged to a file, or even amend, to a telemetry tool like Awarding Insights.
viii. Test for memory leaks
It's a neat practice to proactively test for memory leaks. And information technology's not that hard. Hither's a short pattern you can use:
[ Test ] void MemoryLeakTest ( ) { var weakRef = new WeakReference ( leakyObject ) // Ryn an operation with leakyObject GC . Collect ( ) ; GC . WaitForPendingFinalizers ( ) ; GC . Collect ( ) ; Affirm . IsFalse ( weakRef . IsAlive ) ; } |
For more in-depth testing, memory profilers similar SciTech's .NET Memory Profiler and dotMemory provide a testing API:
MemAssertion . NoInstances ( typeof ( MyLeakyClass ) ) ; MemAssertion . NoNewInstances ( typeof ( MyLeakyClass ) , lastSnapshot ) ; MemAssertion . MaxNewInstances ( typeof ( Bitmap ) , 10 ) ; |
Summary
Don't know about you, just my new year's day's resolution is: Better memory management.
I hope this mail service gave you some value and I'd love information technology if y'all subscribe to my web log or go out a comment below. Any feedback is welcome.
Enjoy the weblog? I would love you lot to subscribe! Performance Optimizations in C#: 10 Best Practices (exclusive article)
Desire to become an good problem solver? Check out a chapter from my book Practical Debugging for .NET Developers
Source: https://michaelscodingspot.com/find-fix-and-avoid-memory-leaks-in-c-net-8-best-practices/
Posted by: benitohoure1990.blogspot.com
0 Response to "How To Find Memory Leaks In Net Application"
Post a Comment