包含有无限循环的线程的对象的垃圾收集
本文关键字:线程 无限循环 包含 对象 | 更新日期: 2023-09-27 18:15:19
假设我有一个类,它做的事情是:
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已被调用,它将被收集吗?
编辑:已解决,模糊性增加了线程安全性。抱歉,当我写这个例子时,我只是虚构了一个场景
(像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...
是的,将收集object data
(假设processData()
没有将其放入列表中)。GC可以处理(托管)线程,如果不能,世界将会结束。
改进建议:
//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);
}
}
编辑:在第二次读取时,这是关于包含对象的。这当然不会被线程收集。
假设"对象"是指拥有该方法的实例,那么如果线程正在执行ProcessInBackground方法,则该实例由线程根(它是arg0,并且您在循环中使用它的字段)。所以不,它不会被收集。
如果"对象"指的是"数据",那么除了最近的对象外,所有对象都当然符合条件。最近的可能取决于编译器配置(它是否消除了本地?)和CLI(它是否检查未读取的本地?)我会说:"它可能是合格的,在发布/优化;
号和_forceStop从未设置为true,对象会被收集吗?
当一个对象的方法处于线程的调用栈中时,该对象不能被垃圾收集。调用栈有对对象的引用。
在这个例子中,ProcessInBackground
是一个线程调用栈上的方法。
Jon的回答纠正了我的答案——垃圾收集器在确信没有更多的引用将被使用时工作,包括this
引用。这意味着,当一个对象在调用堆栈上有一个方法时,它可以被收集(这个方法可以使用其他方法,但不使用这个实例自己的任何成员)。
在我自己的代码中,没有终结器。当它们被收集起来的时候,我很少在乎它们。当我期望收集这些时,它们不在调用堆栈上。如果它们在调用堆栈上被收集,因为。net认为这是一件好事,对我来说没有问题。
我不认为这个细节会改变我应该如何写任何代码,所以我会选择继续写代码,就像我上面错误的事实是真的一样,同时稍微注意到它的错误。如果我的情况和你的相似,你也可以这样做。