Reputation: 29889
I'm using a COM object from .NET using interop. The object basically fetch data from socket and fire some events for .NET layer to process. However, after a while, the COM object stops firing events which revealed later to be because it's collected by GC.
The structure of source code is similar to this one below:
static void Main(string[] args)
{
MyEventGen gen = new MyEventGen();
WeakReference wr = new WeakReference(gen);
gen.ReceiveDataArray +=
new _IMyEventGenEvents_ReceiveDataArrayEventHandler(gen_ReceiveDataArray);
while (true)
{
Thread.Sleep(1000);
Console.WriteLine(wr.IsAlive);
}
}
static void gen_ReceiveDataArray(ref Array indices, ref Array values)
{
// do nothing
}
What I know so far:
From what I understand, object gen
shouldn't be garbage-collected in any way. Since the object is still active in the Main
scope. But the result so far shows that the object was collected by GC.
The object is only garbage-collected when built as Release and run without debugging. Running the Debug builds / running both modes under debugger are fine.
The program will print the first "False" precisely after the first Gen #0 Collection.
By accessing the object in the while
loop, e.g. Console.WriteLine(gen.ToString())
, prevent it from being GC'd!
By adding another static field of Program
class to keep its reference also prevent it from GC'd.
Trying with different load of data, I found that GC only collects the object when Private Bytes reach over the threshold of ~ 3X MBs.
Checking with CLRProfiler, mentioned object was GC'd as suspected.
Have I missed some important .NET GC's concepts? Is it possible to obtain the reason for object being GC'd? Is this, possibly, a known GC bug?
I'm using VS 2008 + .NET 3.5 SP1. Appreciate your thoughts. Thanks!
Upvotes: 6
Views: 1968
Reputation: 1038730
No need to use a COM object to reproduce this. Consider the following:
public class MyEventGen
{
public event Action ReceiveDataArray;
}
class Program
{
public static void Main()
{
var gen = new MyEventGen();
var wr = new WeakReference(gen);
// this is the last time we access the
// gen instance in this scope so after this line it
// is eligible for garbage collection
gen.ReceiveDataArray += new Action(gen_ReceiveDataArray);
// run the collector
GC.Collect();
while (true)
{
Thread.Sleep(1000);
Console.WriteLine(wr.IsAlive);
}
}
static void gen_ReceiveDataArray()
{
}
}
Run it in Release mode and it will exhibit the same behavior. The gen
object falls out of scope and is garbage collected because there is nothing to keep it alive during the execution of the loop.
Upvotes: 7
Reputation: 54734
Your COM objects are eligible for destruction as soon as their .NET equivalents disappear from the program. The last time gen
is used is when the ReceiveDataArray += ...
call is made.
You should add a call to GC.KeepAlive(gen)
at the point in the program where it's acceptable for cleanup to take place. Alternatively, if it needs to stay alive until the program exits, you could add it as a static
field.
Upvotes: 2
Reputation: 5452
Neither gen
nor wr
remain active in the Main scope during the execution of your while
loop.
You enclosed the first three lines in braces which puts those variables in a child scope that is exited before your while
loop begins.
Upvotes: 2