我正在使用来自.NET的COM对象使用互操作。该对象基本上从socket获取数据并触发一些事件以供.NET层处理。但是,过了一会儿,COM对象会停止触发事件,这些事件后来显示是因为它是由GC收集的。
源代码的结构类似于下面这个:
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
}
到目前为止我所知道的:
根据我的理解,对象gen
不应以任何方式进行垃圾收集。由于该对象在Main
范围内仍处于活动状态。但到目前为止的结果显示该对象是由GC收集的。
当构建为发布并在不调试的情况下运行时,该对象仅被垃圾收集。在调试器下运行Debug构建/运行两种模式都可以。
程序将在第一代Gen#0 Collection 之后准确打印第一个“False”。
访问while
循环中的对象,例如Console.WriteLine(gen.ToString())
,阻止它进行GC!
通过添加Program
类的另一个静态字段来保持其引用,也可以阻止它进行GC。
尝试使用不同的数据加载时,我发现GC只在私有字节超过~3X MB的阈值时收集对象。
使用CLRProfiler进行检查时,提到的对象被GC怀疑为。
我是否错过了一些重要的.NET GC概念?是否有可能获得对象GC'd的原因?这可能是一个已知的GC错误吗?
我正在使用VS 2008 + .NET 3.5 SP1。欣赏你的想法。谢谢!
答案 0 :(得分:7)
无需使用COM对象来重现此问题。请考虑以下事项:
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()
{
}
}
在发布模式下运行它会表现出相同的行为。 gen
对象超出范围并被垃圾收集,因为在执行循环期间没有任何东西可以使它保持活动状态。
答案 1 :(得分:2)
在执行gen
循环期间,wr
和while
都未在主范围内保持活动状态。
您将前三行括起来,将这些变量放在while
循环开始之前退出的子作用域中。
答案 2 :(得分:2)
只要.NET对象从程序中消失,您的COM对象就有资格进行销毁。上次gen
的使用时间是ReceiveDataArray += ...
来电。{/ p>
您应该在程序中的某个位置添加对GC.KeepAlive(gen)
的调用,以便进行清理。或者,如果它需要保持活着直到程序退出,您可以将其添加为static
字段。