我正在使用来自.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收集的.
>该对象仅在构建为Release时进行垃圾收集,并且无需调试即可运行.在调试器下运行Debug构建/运行两种模式都可以.
>程序将在第一代Gen#0 Collection之后精确打印第一个“False”.
>通过访问while循环中的对象,例如Console.WriteLine(gen.ToString()),防止它被GC!
>通过添加Program类的另一个静态字段来保持其引用也阻止它进行GC’d.
>尝试使用不同的数据加载,我发现GC只在私有字节超过~3X MB的阈值时才收集对象.
>检查CLRProfiler,提到的对象被GC怀疑.
我是否错过了一些重要的.NET GC概念?是否有可能获得对象GC’d的原因?这可能是一个已知的GC错误吗?
我正在使用VS 2008 .NET 3.5 SP1.欣赏你的想法.谢谢!
解决方法
无需使用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对象超出范围并被垃圾收集,因为在循环执行期间没有任何东西可以使它保持活动状态.