包含具有无限循环的线程的对象的垃圾收集

时间:2011-11-20 21:31:46

标签: c# garbage-collection

假设我有一个类似于:

的课程
 public class Foo
 {
     private bool _forceStop = false;
     private Queue<object> queue;
     private void ProcessInBackground()
     {
          while(!forceStop )
          {
              Moniter.Enter(queue);
              while(!_forceStop && queue.Count == 0)Moniter.Wait(queue);
              object data = null;                 
              if (!_forceStop)
                 data = queue.Dequeue(); 
              Moniter.Exit(queue);

              if (data != null)
                 processData(data);
           }
      }

      ...
  }

如果不再使用Foo类的对象,并且在该对象中_forceStop永远不会设置为true并且假设已调用ProcessInBackground,那么它是否会被收集?

编辑:已解决,歧义增加了线程安全性。对不起,当我编写示例时,我刚刚编写了一个场景。

4 个答案:

答案 0 :(得分:5)

(和Marc一样,我假设你指的是你调用ProcessInBackground的对象。我也假设queue是一个字段。)

不 - 你仍然指的是queue,这意味着中的字段将被读取,这意味着包含的对象不能被垃圾收集。

请注意,仅仅因为对象中运行的方法阻止垃圾收集 - 垃圾收集器只关心是否有可能使用引用本身,或者对象中的字段正在阅读演示代码显示:

using System;
using System.Threading;

public class OddFinalizer   
{
    int field;

    public OddFinalizer(int field)
    {
        this.field = field;
    }

    ~OddFinalizer()
    {
        Console.WriteLine("OddFinalizer");
    }

    public void Work()
    {
        for (int i = 0; i < 5; i++)
        {
            Console.WriteLine("In loop before last access...");
            GC.Collect();
            GC.WaitForPendingFinalizers();            
            Thread.Sleep(1000);
        }
        Console.WriteLine("Field value: {0}", field);
        for (int i = 0; i < 5; i++)
        {
            Console.WriteLine("In loop after last access...");
            GC.Collect();
            GC.WaitForPendingFinalizers();
            Thread.Sleep(1000);
        }
    }

    static void Main(string[] args)
    {
        new OddFinalizer(10).Work();
    }
}

结果(使用/ o +编译,在调试器中运行):

In loop before last access..
In loop before last access..
In loop before last access..
In loop before last access..
In loop before last access..
Field value: 10
In loop after last access...
OddFinalizer
In loop after last access...
In loop after last access...
In loop after last access...
In loop after last access...

答案 1 :(得分:3)

是的,将收集object data(假设processData()没有将其放入列表中)。 GC可以处理(托管)线程,如果不能,世界将会结束。

但是你的代码是线程安全,你的DeQueueing放弃锁定后。

改善建议:

//untested
private bool _forceStop = false;
private object _locker = new object();  // better not depend on queue here

private void ProcessInBackground()
{
    while(true)
    {
       // Moniter.Enter(queue);
       lock(_locker)
       {       
          while(!_forceStop && queue.Count == 0)
            Moniter.Wait(_locker);

          //Moniter.Exit(queue);
          if (_forceStop) break;

          object data = queue.Dequeue(); 
       }
       processData(data);
    }
}

编辑:

第二次阅读时,这是关于包含对象的。这当然不受线程的收集。

答案 2 :(得分:1)

假设“对象”是指拥有该方法的实例,那么如果一个线程正在执行ProcessInBackground方法,那么该实例将以该线程为根(它是arg0,并且您正在使用它中的字段环)。所以不,它不会被收集。

如果“对象”是指“数据”,那么除了最近的所有对象都肯定符合条件。最新的可能取决于编译器配置(它是否消除了本地?)和CLI(它是否检查未读取的本地?)。我会说“它可能符合条件,在发布/优化;可能不在调试/未优化”。

答案 3 :(得分:1)

  

和_forceStop永远不会设置为true,是否会收集对象?

没有

当一个对象的某个方法位于线程的callstack上时,不能对其进行垃圾回收。 callstack有对象的引用。

在这种情况下,ProcessInBackground是线程的callstack上的方法。


Jon的回答纠正了我的回答 - 当垃圾收集器确信不再使用引用时,包括this引用。这意味着可以在callstack上有一个方法时收集一个对象(这个方法可能正在使用其他方法,但不使用任何这个实例自己的成员)。

在我自己的代码中,我没有终结器。我收集的东西很少。当我希望收集它们时,它们就不会在那个时候出现在callstack上。如果他们在callstack上被收集,因为.net认为这是件好事,我没问题。

我认为细节不会改变我应该编写任何代码的方式,所以我会选择继续编写代码,好像我上面的错误事实是真的,同时稍微注意它的错误。如果我的情况与您的相似,您也可以这样做。