Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How might I identify unwanted .NET objects that aren't being garbage-collected?

My (C# .NET 4.0) application runs over a period of days, updating a simulated account according to changes in prices acquired from a SQLite database.

All I need on any particular date is the account in its current state and the latest prices. I'd expect that the Garbage Collector would keep memory usage on a fairly even keel: what I'm seeing is a steady increase in Working Set and Private Memory (as reported by System.Diagnostics.GetCurrentProcess()) and also in GC.GetTotalMemory(true): about 300K per day in this case. Inevitably, the whole thing crashes after about 12 simulated years, at which point the memory use has increased by about 1GB.

Memory usage increases more-or-less linearly, (very much more smoothly if I force a GC.Collect() at the end of each day).

I'm inferring that some objects are somehow not garbage-collectible, even when I think there's no further need for them and had expected that they would be cleaned up in the normal ebb and flow of execution.

What might I try to identify where I've inadvertently managed to create such a situation?

I've downloaded and run CLRProfiler - it's going to take the best part of the weekend to digest the documentation, though, and there's no guarantee that it'll be able to help.

I'm working through the references in this question. In general, I know what kind of situation can be causing the problem, I'm more interested to see if there are faster ways to identify the specifics without spending precious days working through my code ticking off references...

NOTE: The problem doesn't appear to be event-related and there's no graphic component involved.

like image 268
Mike Woodhouse Avatar asked Dec 02 '11 16:12

Mike Woodhouse


People also ask

How do you know if an object is garbage collected?

An object is eligible to be garbage collected if its reference variable is lost from the program during execution. Sometimes they are also called unreachable objects.

How garbage collector knows that the object is not in use and needs to be removed C#?

How garbage collector knows that the object is not in use and needs to be removed? Answer: Garbage collector reclaims objects that are no longer being used, clears their memory, and keeps the memory available for future allocations. This is done via bookkeeping the references to the objects.

Does .NET have garbage collection?

. NET's garbage collector manages the allocation and release of memory for your application. Each time you create a new object, the common language runtime allocates memory for the object from the managed heap.

How does garbage collector know which objects to free?

When the garbage collector performs a collection, it releases the memory for objects that are no longer being used by the application. It determines which objects are no longer being used by examining the application's roots.


3 Answers

You said you can simulate workload to cause your application to crash. To quickly find your leak, simulate workload until memory consumption becomes significant (e.g. 100 MB), attach WinDbg or VS with unmanaged debugging to your process and run in immediate window:

.load sos
extension C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\sos.dll loaded
!dumpheap -stat
total 2885 objects
Statistics:
MT    Count    TotalSize Class Name
7a5eab18        1           12 System.Diagnostics.TraceOptions
(lots of unimportant stuff here)
79332b54      304         7296 System.Collections.ArrayList
79333274       56        11640 System.Collections.Hashtable+bucket[]
793042f4      345        50056 System.Object[]
79330b24     1041        99428 System.String
79332cc0       21    107109728 System.Int32[]

From this (sorted) output you can tell you are having a problem with int[] type. Type:

!DumpHeap -type System.Int32[] (or whichever type you got)
Address       MT     Size
01381a1c 7931e9bc       40     
(cut)
075d1000 79332cc0 67108880     
03811000 79332cc0 40000016     
total 22 objects
Statistics:
MT    Count    TotalSize Class Name
7931e9bc        1           40 System.Int32[][]
79332cc0       21    107109728 System.Int32[]
Total 22 objects

Now you have address of your object(s). Pick one of them and run

!GCRoot 075d1000

And you'll get cause of your leak. Also, you might want to consult this tutorial.

like image 64
MagnatLU Avatar answered Sep 23 '22 19:09

MagnatLU


Are you using events? If object A is subscribed to an event on object B, then B is storing a reference to A. Therefore A can't be garbage collected until B can be garbage collected. If A unsubscribes from B, then B's reference to A is removed.

like image 25
Greg Avatar answered Sep 26 '22 19:09

Greg


One thing that I'd suggest looking for as a heuristic is places where you might have attached to an event in a longer lived instance from a shorter lived one. If you have some instance that sticks around for the lifetime of your application, and you have shorter lived classes that listen for events on it, failing to unhook those events will result in your shorter-lived objects being skipped by the garbage collector, since the longer lived object hangs onto a reference to them until you detach.

Edit: for what it's worth, you might want to check out Ants Memory Profiler. I don't know how it compares to the one you're looking at it, but from having tried it out, the learning curve on it isn't too bad, so you might spend less time figuring it out.

like image 41
Erik Dietrich Avatar answered Sep 24 '22 19:09

Erik Dietrich