What happens to 'object' after local scope? - c#

I am eager to know what happens to a object in .NET when it falls out of scope. Something like this:
class A
{
ClassB myObjectB = new ClassB();
}
//what happens to object at this point here?
What happens in the memory? Does GC get called when it falls out of scope and loose it reference in the heap?

What happens in the memory? Does GC get called when it falls out of scope and loose it reference in the heap?
No - the GC doesn't get called at this point.
What happens is that the object is left in memory, but is now "unrooted" - basically, there are no references to that object from any other object. As such, the object is now eligible for garbage collection.
At some point in the future, the GC will run. When this happens, the unrooted object will now be eligible for garbage collection. Depending on which generation holds the object, it may or may not be cleaned at that point. Eventually, if the program continues executing and if memory pressure causes the appropriate generation to be collected, the memory for that object will be reclaimed.
There is no definite point in time when this will happen (unless you explicitly call GC.Collect, which is a bad idea). However, with managed code, you don't really need to worry about when this will happen. Just assume that it will happen when and if appropriate for your specific application.

Following from pst's comment, a better example might be this:
void M()
{
ClassB myObjectB = new ClassB();
}
//what happens to object at this point here?
In this example, myObjectB is a local variable rather than a field. So, what happens when the local variable goes out of scope? Nothing! Scope has nothing to do with object lifetime in C#.
What really happens is that the JIT compiler decides to release the object at some point. That can be before the end of the variable's scope, if the variable is not going to be used in the rest of the method. Once the object is no longer referenced, as other answers have also mentioned, it becomes eligible for collection by the GC. It is not actually collected until the GC runs (actually, until the GC collects the generation in which the object is living).
As pst implied, a field is a poor example because it will always be reachable whenever its containing object is reachable, so the separation between scope and object lifetime is even greater:
class A
{
private object o = //whatever
}
void B()
{
var a = new A();
// here, the field o is not in scope, but the object it refers to is reachable and cannot be collected.
GC.KeepAlive(a);
}

I find "falling out of scope" to be a much more C++-specific way of thinking of things, where at the end of a given scope an object with automatic storage is freed and has its destructor called.
In the C# world, there's no "falling out of scope". Variables (read: names) exist in a certain scope, and that's it. The GC really has no concern for this; an object can be collected before the end of its name's scope even exits, or long afterwards depending on what references it and when the GC decides a collection is necessary.
The two concepts should then be divorced and reasoned about separately. Scoping is all about names, whereas garbage collection cares only about the reachability of objects. When an object is no longer reachable from one of the known roots, it will be scheduled for collection.

If there's nothing referencing the object, it gets eventually collected by GC. The thing is, you can't predict WHEN exactly it will happen. You just know, it will happen.

Generally speaking, Garbage Collection happens at 3 distinct generations (0, 1, or 2). When each of these is collected depends on how much resources are needed by the OS.
A call to GC.Collect() will collect all of the available resources but it is possible to define which generation of resources to collect.

Related

How can my Main() method be holding references to variables that have been disposed and are no longer used?

I'm working on fixing memory leaks in a large project, and so I've shrunk it down into one Main() method wherein a reference type object Obj1 containing a reference to another reference type object Obj2. Then, I create another object of type Obj1 which contains a reference to the same Obj2 object. Both of the objects are in their own using blocks, like so:
using (dynamic obj1_a = new Obj1(args))
{
do some actions...
using (dynamic obj1_b = new Obj1(args))
{
do some more actions...
//Memory Snapshot 1 taken here
}
}
GC.Collect();
GC.WaitForPendingFinalizer();
GC.Collect();
//Memory Snapshot 2 taken here
Somehow, when I take the 2 snapshots at the points commented above and compare them, .NET Memory Profiler indicates that even though the two objects obj1_a and obj1_b have been disposed, they haven't been GC'ed. When I examine the reference graphs, I see that the memory profiler says that both objects are referenced by my Main() method itself. I've gone through the whole code of the Main() method (it's not very complex, just creating, slightly modifying and then testing for garbage-collection) to see if there is a variable reference remaining to these two objects but there are none. How is it still possible that my Main() method could be holding these objects in memory? It's important that they get garbage collected (or at least are able to get GC'ed) because they contain references to many more reference and value types and the program becomes quite a memory drain without it.
even though the two objects obj1_a and obj1_b have been disposed, they haven't been GC'ed
Your statement presupposes that there is a connection between an object being disposed and it being deallocated by the garbage collector. Make sure you fully understand the following statement: disposing an object has no effect whatsoever on whether it is eligible to be collected. From the GC's perspective, Dispose is just a method. You might as well say "even though I called ToString on an object, it still hasn't been GC'd". What does ToString have to do with the GC? Nothing. What does Dispose have to do with the GC? Nothing whatsoever.
Now, this slightly overstates the case. A finalizable object should implement IDisposable, and its Dispose should call SuppressFinalization. That has an effect on the garbage collector because finalizable objects always live at least one collection longer than they otherwise would because the finalization queue is a root. But here the effect is not due directly to Dispose; it is simply a convention that disposers suppress finalization. It is the suppression which has an effect on the GC behaviour.
How is it still possible that my Main() method could be holding these objects in memory?
An object is collected by the GC when the GC determines that there is no living root containing a direct or indirect reference to the object.
A local variable in an active method is a living root.
The runtime is permitted to, entirely at its discretion and for any reason whatsoever, to both (1) determine that a local is never read again and treat it as dead early, and (2) keep a local alive longer even when control has passed beyond the local variable declaration space of that variable.
Even though your obj1_a and obj1_b are out of scope by the time the GC runs, the runtime is entirely permitted to pretend that they were declared at the top scope of Main, and is permitted to keep them alive until Main completes, which is after the GC runs.
It's important that they get garbage collected (or at least are able to get GC'ed) because they contain references to many more reference and value types and the program becomes quite a memory drain without it.
If you require fine-grained control over lifetimes of objects then languages which have automatic memory deallocation do not meet your requirements.

Possible to force an object to be garbage collected in Gen 1 or Gen 2 and not in Gen 0?

An interviewer asked me a weird question of which I couldn't find answer.
Is it possible to force an object to be garbage collected in Gen 1 or Gen 2 and not in Gen 0?
Yes.
public class WillAlwaysBeCollectedInGen1or2
{
~WillAlwaysBeCollectedInGen1or2()
{
}
}
Because it has a finaliser, and no code that ever calls GC.SuppressFinalize(this), then it will always end up in the finalisation queue instead of being collected when it first becomes eligible for collection, which means it will always be collected in a generation other than the first.
Of course, since we generally want the opposite to happen (objects are collected as soon as possible) this shows why one should not define a finaliser unless it's actually needed, and should always call GC.SuppressFinalize(this) if an object is in a state where finalisation doesn't do anything useful (most obviously if it's been disposed of explicitly, but there can be other cases).
So, the usefulness of this knowledge is this; in knowing what not to do.
By extension, you can force an arbitrary object to be collected in Gen1 or Gen2 only by holding a strong reference to it in such an object:
public class PreventGen0Collection
{
private object _keptAlive;
public PreventGen0Collection(object keptAlive)
{
_keptAlive = keptAlive;
}
~PreventGen0Collection()
{
}
}
Because PreventGen0Collection is itself prevented from collection until at least Gen 1, as explained above, the object passed to its constructor is kept from even being eligible for collection on a GC sweep, and will hence be promoted to the next generation.
Again, this demonstrates code to avoid; being finalisable causes not just an object to be promoted, but a whole graph. Never use finalisers when you don't need to, and suppress finalisation when you can.
You can't... But you can extend the life of an object until some point. The simplest way inside a method is to have a:
var myObject = new MyObject();
... some code
... some other code
// The object won't be colleted until this point **at least**
GC.KeepAlive(myObject)
Or you can use GCHandle:
var myObject = new MyObject();
this.Handle = GCHandle.Alloc(new object(), GCHandleType.Normal);
(where this.Handle is a field of your object)
and perhaps in another method:
this.Handle.Free();
The description of GCHandleType.Normal is:
You can use this type to track an object and prevent its collection by the garbage collector. This enumeration member is useful when an unmanaged client holds the only reference, which is undetectable from the garbage collector, to a managed object.
Note that if you do this (the GCHandle "this") inside a class, you should implement the IDisposable pattern to free the this.Handle.
Ok... Technically you could:
var myObject = new MyObject();
GC.Collect(0); // collects all gen0 objects... Uncollected gen0 objects become gen1
GC.KeepAlive(myObject);
Now myObject is gen1... But this won't really solve your problem :-)
According to the documentation of GC.Collect:
Forces an immediate garbage collection of all generations.
Then yes, you can do that. Simply force a collection.
As described here, Fundamentals of Garbage Collection, under Survival and promotions:
Objects that are not reclaimed in a garbage collection are known as survivors, and are promoted to the next generation.
As such, if the object was in generation 0 before you forced a collection, it will now be in generation 1.
OK, and then, what if I don't have a reference to the object any more? What happens if I force a collection then? Well, most likely it will be collected. Can it be fixed? Well, depends on what you mean by "fixed", but yes.
Add a finalizer to the object. A finalizer will make the object be moved on to a separate list during collection, ready for the finalizer thread to finalize it. Only after the finalizer has processed it will it again be eligible for collection but by then it has already been promoted.
Also note that if you add the finalizer because you need to handle the case of not having a reference you don't really need to force the collection any more since this is an interview question with a very specific problem, you have now guaranteed that it will survive generation 0.
So two steps, either of which will guarantee that the object is not collected in generation 0:
Add a finalizer
Hold on to a reference to the object and force a collection.
Caveat: Garbage collection is documented, but have been tuned various ways and times during the life of .NET. Whether the documentation and above text can be taken as documentation fixed in stone or whether there are optimizations done here that can for instance make GC.Collect not force a collection because a collection is already in progress I don't know.
Since you asked in the context of an interview I would say the above should be your answer to the interviewer, but don't rely on any of this in practice.
GUIDELINE: Relying on the internals of the garbage collectors is one way to make brittle code.
DON'T DO IT.
The only place I've seen good reasons for actually messing with GC.Collect, and even there it is dubious, is in a more or less single-threaded system that deals in big batches. For instance a Windows service that processes big batches of data periodically could do one batch, then force a collection to reduce memory footprint until the next batch comes due.
However, this is a highly specialized case. Other than satisfying an interviewer, GC.Collect should not (as a guideline) be present in your production code.

Garbage Collection - Is it always safe to access objects via strong references?

My understanding is that when the GC finds a sub-graph of objects that are no longer accessible (via strong references) from the main graph, it will collect them up and reclaim the memory. My question is concerning the order in which inaccessible objects are deleted. Does this occur as an atomic operation? Are all of the inaccessible objects finalized at once, or does the GC finalize each object one-by-one while the application is still executing? If the objects are finalized one-by-one, is there a particular order that is followed?
If I have an object A that holds a weak reference to object B, then it is clear that A must check if B is still alive before invoking any of B’s instance methods. Now suppose B holds a strong reference to another object C. If B is still alive, am I always guaranteed that C will also still be alive? Is there any possibility that the GC might have marked both B & C for collection, but C is finalized before B?
My guess is that it will always be safe to access C from B (since this is a strong reference). But I would like to confirm this assumption, because if I am wrong I could introduce a very intermittent hard-to-track-down bug.
public class ClassA
{
private readonly WeakReference objBWeakRef;
public ClassA(ClassB objB)
{
objBWeakRef = new WeakReference(objB);
}
public void DoSomething()
{
// The null check is required because objB
// may have been cleaned-up by the GC
var objBStrongRef = (ClassB) objBWeakRef.Target;
if (objBStrongRef != null)
{
objBStrongRef.DoSomething();
}
}
}
public class ClassB
{
private readonly ClassC objCStrongRef;
public ClassB(ClassC objC)
{
objCStrongRef = objC;
}
public void DoSomething()
{
// Do I also need to do some kind of checking here?
// Is it possible that objC has been collected, but
// the GC has not yet gotten around to collecting objB?
objCStrongRef.DoSomething();
}
}
public class ClassC
{
public void DoSomething()
{
// do something here... if object is still alive.
}
}
Yes, if your ClassB has a reference to a ClassC instance via objCStrongRef, then if the ClassB is still alive you don't need to worry about the ClassC evaporating at random. The exception to this is if you write a finalizer, i.e.
~ClassB() { ...}
In there, you should not attempt to talk to objCStrongRef at all; because you can have no clue which object gets finalized first. If ClassC needs something at finalization, it should have a separate ~ClassC() - although in reality finalizer methods are really rare and you shouldn't add them without good reason (unmanaged handles, etc)
Re finalization order: no, there is no particular order followed, because full loops are possible - and it needs to be broken somewhere arbitrarily.
In normal managed methods, you can rely on everything that B refers to with strong references to be alive as long as B is alive. The reference to C from B will be good for the lifetime of B.
The exception is in finalization code. If you implement a finalizer (which should be considered an unusual case, not the norm), then all bets are off within the call chain of the finalizer. Finalizers may not execute for a long time after the GC notices that the objects are no longer referenced by live objects - objects with finalizers tend to hang around a lot longer than "normal" objects, which is another reason not to use finalizers. A finalizer may not execute at all, ever, in extreme panic shutdown corner cases. You can't assume anything about what thread the finalizer will execute on, or what order finalizers will execute in. And you should avoid referring to any reference variables in your object because you don't know if they have already been finalized.
This is all academic though. Your code example doesn't implement a finalizer, so you don't need to worry about the bizzarro finalizer world.
From the "CLR via C#" by Jeffrey Richter:
Furthermore, be aware of the fact that you have no control over when
the Finalize method will execute. Finalize methods run when a garbage
collection occurs, which may happen when your application requests
more memory. Also, the CLR doesn’t make any guarantees as to the order
in which Finalize methods are called, so you should avoid writing a
Finalize method that accesses other objects whose type defines a
Finalize method; those other objects could have been finalized
already. However, it is perfectly OK to access value type instances or
reference type objects that do not define a Finalize method. You also
need to be careful when calling static methods because these methods
can internally access objects that have been finalized, causing the
behavior of the static method to become unpredictable.
If there exists no reference path of any sort which can reach an object, there will be no means to examine the memory space that object used to occupy, unless or until such time as the GC has run and made that memory space usable for reuse and some new object is created that uses it; by the time that occurs, the old object won't exist anymore. Regardless of when the GC runs, an object becomes instantly inaccessible the moment the last reference to it is destroyed or becomes inaccessible.
Objects with active finalizers always have a reference path, since a data structure called the "finalization queue" holds a reference to every such object. Objects in this queue are the last things processed by the GC; if an object is found to be referenced by the queue but by nothing else, a reference will be stored in a structure called the "freachable" queue, which lists objects whose Finalize method should be run at first opportunity. Once a GC cycle completes, this list will be considered a strong rooted reference, but the finalizer thread will start pulling things out of it. Typically, once items get pulled from the list there won't be any reference to them anywhere and they'll disappear.
Weak references add another wrinkle, since objects are considered eligible for collection even if weak references to them exist. The simplest way to regard those as working is to figure that once every object requiring retention has been identified, the system will go through and invalidate every WeakReference whose targets do not require retention. Once every such WeakReference instances has been invalidated, the object will be inaccessible.
The only situation where atomic semantics might matter would be when two or more WeakReference instances target the same object. In that scenario, it might theoretically be possible for one thread to access the Target property of a WeakReference at the exact moment that the GC was invalidating another WeakReference which had the same target. I don't think this situation can actually arise; it could be prevented by having multiple WeakReference instances that share the same target also share their GCHandle. In that case, either the access to the target would happen soon enough to keep the object alive, or else the handle would be invalidated, effectively invalidating all WeakReference instances that hold a reference to it.

What is the correct way to free memory in C#

I have a timer in C# which executes some code inside it's method. Inside the code I'm using several temporary objects.
If I have something like Foo o = new Foo(); inside the method, does that mean that each time the timer ticks, I'm creating a new object and a new reference to that object?
If I have string foo = null and then I just put something temporal in foo, is it the same as above?
Does the garbage collector ever delete the object and the reference or objects are continually created and stay in memory?
If I just declare Foo o; and not point it to any instance, isn't that disposed when the method ends?
If I want to ensure that everything is deleted, what is the best way of doing it:
with the using statement inside the method
by calling dispose method at the end
by putting Foo o; outside the timer's method and just make the assignment o = new Foo() inside, so then the pointer to the object is deleted after the method ends, the garbage collector will delete the object.
1.If I have something like Foo o = new Foo(); inside the method, does that
mean that each time the timer ticks,
I'm creating a new object and a new
reference to that object?
Yes.
2.If I have string foo = null and then I just put something temporal in foo,
is it the same as above?
If you are asking if the behavior is the same then yes.
3.Does the garbage collector ever delete the object and the reference or
objects are continually created and
stay in memory?
The memory used by those objects is most certainly collected after the references are deemed to be unused.
4.If I just declare Foo o; and not point it to any instance, isn't that
disposed when the method ends?
No, since no object was created then there is no object to collect (dispose is not the right word).
5.If I want to ensure that everything is deleted, what is the best way of
doing it
If the object's class implements IDisposable then you certainly want to greedily call Dispose as soon as possible. The using keyword makes this easier because it calls Dispose automatically in an exception-safe way.
Other than that there really is nothing else you need to do except to stop using the object. If the reference is a local variable then when it goes out of scope it will be eligible for collection.1 If it is a class level variable then you may need to assign null to it to make it eligible before the containing class is eligible.
1This is technically incorrect (or at least a little misleading). An object can be eligible for collection long before it goes out of scope. The CLR is optimized to collect memory when it detects that a reference is no longer used. In extreme cases the CLR can collect an object even while one of its methods is still executing!
Update:
Here is an example that demonstrates that the GC will collect objects even though they may still be in-scope. You have to compile a Release build and run this outside of the debugger.
static void Main(string[] args)
{
Console.WriteLine("Before allocation");
var bo = new BigObject();
Console.WriteLine("After allocation");
bo.SomeMethod();
Console.ReadLine();
// The object is technically in-scope here which means it must still be rooted.
}
private class BigObject
{
private byte[] LotsOfMemory = new byte[Int32.MaxValue / 4];
public BigObject()
{
Console.WriteLine("BigObject()");
}
~BigObject()
{
Console.WriteLine("~BigObject()");
}
public void SomeMethod()
{
Console.WriteLine("Begin SomeMethod");
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("End SomeMethod");
}
}
On my machine the finalizer is run while SomeMethod is still executing!
The .NET garbage collector takes care of all this for you.
It is able to determine when objects are no longer referenced and will (eventually) free the memory that had been allocated to them.
Objects are eligable for garbage collection once they go out of scope become unreachable (thanks ben!). The memory won't be freed unless the garbage collector believes you are running out of memory.
For managed resources, the garbage collector will know when this is, and you don't need to do anything.
For unmanaged resources (such as connections to databases or opened files) the garbage collector has no way of knowing how much memory they are consuming, and that is why you need to free them manually (using dispose, or much better still the using block)
If objects are not being freed, either you have plenty of memory left and there is no need, or you are maintaining a reference to them in your application, and therefore the garbage collector will not free them (in case you actually use this reference you maintained)
Let's answer your questions one by one.
Yes, you make a new object whenever this statement is executed, however, it goes "out of scope" when you exit the method and it is eligible for garbage collection.
Well this would be the same as #1, except that you've used a string type. A string type is immutable and you get a new object every time you make an assignment.
Yes the garbage collector collects the out of scope objects, unless you assign the object to a variable with a large scope such as class variable.
Yes.
The using statement only applies to objects that implement the IDisposable interface. If that is the case, by all means using is best for objects within a method's scope. Don't put Foo o at a larger scope unless you have a good reason to do so. It is best to limit the scope of any variable to the smallest scope that makes sense.
Here's a quick overview:
Once references are gone, your object will likely be garbage collected.
You can only count on statistical collection that keeps your heap size normal provided all references to garbage are really gone. In other words, there is no guarantee a specific object will ever be garbage collected.
It follows that your finalizer will also never be guaranteed to be called. Avoid finalizers.
Two common sources of leaks:
Event handlers and delegates are references. If you subscribe to an event of an object, you are referencing to it. If you have a delegate to an object's method, you are referencing it.
Unmanaged resources, by definition, are not automatically collected. This is what the IDisposable pattern is for.
Finally, if you want a reference that does not prevent the object from getting collected, look into WeakReference.
One last thing: If you declare Foo foo; without assigning it you don't have to worry - nothing is leaked. If Foo is a reference type, nothing was created. If Foo is a value type, it is allocated on the stack and thus will automatically be cleaned up.
Yes
What do you mean by the same? It will be re-executed every time the method is run.
Yes, the .Net garbage collector uses an algorithm that starts with any global/in-scope variables, traverses them while following any reference it finds recursively, and deletes any object in memory deemed to be unreachable. see here for more detail on Garbage Collection
Yes, the memory from all variables declared in a method is released when the method exits as they are all unreachable. In addition, any variables that are declared but never used will be optimized out by the compiler, so in reality your Foo variable will never ever take up memory.
the using statement simply calls dispose on an IDisposable object when it exits, so this is equivalent to your second bullet point. Both will indicate that you are done with the object and tell the GC that you are ready to let go of it. Overwriting the only reference to the object will have a similar effect.
The garbage collector will come around and clean up anything that no longer has references to it. Unless you have unmanaged resources inside Foo, calling Dispose or using a using statement on it won't really help you much.
I'm fairly sure this applies, since it was still in C#. But, I took a game design course using XNA and we spent some time talking about the garbage collector for C#. Garbage collecting is expensive, since you have to check if you have any references to the object you want to collect. So, the GC tries to put this off as long as possible. So, as long as you weren't running out of physical memory when your program went to 700MB, it might just be the GC being lazy and not worrying about it yet.
But, if you just use Foo o outside the loop and create a o = new Foo() each time around, it should all work out fine.
As Brian points out the GC can collect anything that is unreachable including objects that are still in scope and even while instance methods of those objects are still executing. consider the following code:
class foo
{
static int liveFooInstances;
public foo()
{
Interlocked.Increment(ref foo.liveFooInstances);
}
public void TestMethod()
{
Console.WriteLine("entering method");
while (Interlocked.CompareExchange(ref foo.liveFooInstances, 1, 1) == 1)
{
Console.WriteLine("running GC.Collect");
GC.Collect();
GC.WaitForPendingFinalizers();
}
Console.WriteLine("exiting method");
}
~foo()
{
Console.WriteLine("in ~foo");
Interlocked.Decrement(ref foo.liveFooInstances);
}
}
class Program
{
static void Main(string[] args)
{
foo aFoo = new foo();
aFoo.TestMethod();
//Console.WriteLine(aFoo.ToString()); // if this line is uncommented TestMethod will never return
}
}
if run with a debug build, with the debugger attached, or with the specified line uncommented TestMethod will never return. But running without a debugger attached TestMethod will return.

Delegate variables not garbage collected

Recently discovered that the variables inside ToGadget, and presumably the delegate as well, weren't getting garbage collected. Can anyone see why .NET holds a reference to this? Seems that the delegate and all would be marked for garbage collection after Foo ends. Literally saw Billions in memory after dumping the heap.
Note: 'result.Things' is a List<Gadget> () and Converter is a System delegate.
public Blah Foo()
{
var result = new Blah();
result.Things = this.Things.ConvertAll((new Converter(ToGadget)));
return result;
}
.................
public static Gadget ToGadget(Widget w)
{
return new Gadget(w);
}
Update: changing the 'ConvertAll' to this cleans up the delegates and corresponding object references. This suggests to me that either List<> ConvertAll is somehow holding on to the delegate or I don't understand how these things are garbage collected.
foreach (var t in this.Things)
{
result.Things.Add(ToGadget(t));
}
Use a memory profiler.
You can ask on StackOverflow all day and get a bunch of educated guesses, or you can slap a memory profiler on your application and immediately see what is rooted and what is garbage. There are tools available that are built specifically to solve your exact problem quickly and easily. Use them!
There is one major flaw in your question, which may be the cause of confusion:
Seems that the delegate and all would be marked for garbage collection after Foo ends.
The CLR doesn't "mark items" for collection at the end of a routine. Rather, once that routine ends, there is no longer an (active) reference to any of the items referenced in your delegate. At that point, they are what is refered to as "unrooted".
Later, when the CLR determines that there is a certain amount of memory pressure, the garbage collector will execute. It will search through and find all unrooted elements, and potentially collect them.
The important distinction here is that the timing is not something that can be predicted. The objects may never be collected until your program ends, or they may get collected right away. It's up to the system to determine when it will collect. This doesn't happen when Foo ends - but rather at some unknown amount of time after Foo ends.
Edit:
This is actually directly addressing your question, btw. You can see if this is the issue by forcing a garbage collection. Just add, after your call to Foo, a call to:
GC.Collect();
GC.WaitForPendingFinalizers();
Then do your checking of the CLR's heap. At this point, if you're still getting objects in the heap, it's because the objects are still being rooted by something. Your simplified example doesn't show this happening, but as this is a very simplified example, it's difficult to determine where this would happen. (Note: I don't recommend keeping this in your code, if this is the case. Calling GC.Collect() manually is almost always a bad idea...)
It looks like your function is set up to return the new Blah(). Is it actually being returned in your code? I see in the piece you posted that it is not. If that is the case, then the new Blah() would have a scope outside of Foo and it may be the calling function that is actually holding the references in scope. Also, you're creating new Gadget() as well. Depending on how many Blahs to Gadgets you have, you could be exponentially filling your memory as the Gadgets will be scoped with the Blahs which are then held in scope beyond Foo.
Whether I'm right or wrong, this possibility was kinda funny to type.

Categories