Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/loops/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 未收集循环变量_C#_Loops_Scope - Fatal编程技术网

C# 未收集循环变量

C# 未收集循环变量,c#,loops,scope,C#,Loops,Scope,我有一个循环变量,尽管超出了范围,但它似乎没有被垃圾收集(根据Red-Gate ANTS memory profiler) 代码如下所示: while (true) { var item = blockingQueue.dequeue(); // blocks until an item is added to blockingQueue // do something with item } 据我所知,在blockingQueue.dequeue()。这是预期的行为,还是内存

我有一个循环变量,尽管超出了范围,但它似乎没有被垃圾收集(根据Red-Gate ANTS memory profiler)

代码如下所示:

while (true)
{
    var item = blockingQueue.dequeue(); // blocks until an item is added to blockingQueue
    // do something with item
}
据我所知,在
blockingQueue.dequeue()。这是预期的行为,还是内存分析器中的错误

其次,如果这是预期的行为,我将如何强制
在循环体的末尾收集?将其设置为
null
似乎不会导致它被收集。这一点很重要,因为队列可能会阻塞很长一段时间,并且
item
引用相当大的对象树

注意,探查器的文档说明在获取内存快照之前执行GC,并且引用不在终结器队列中

我能够用代码重现同样的问题

更新


gist中的代码有点缺陷,因为它合法地保留了
GetFoo()
中的引用。更改后,当显式设置为
null
时,对象现在会被收集。然而,我相信Hans的回答解释了我在实际代码中看到的情况。

直到调用了
出列
,项目的值才被覆盖,仍然在使用,对吗?您最好将其设置为null,即调用GC.Collect(),但您不能保证收集该变量,也无法强制收集该变量,所以为什么要这么麻烦呢

while (true)
{
    {
        var item = blockingQueue.dequeue(); // blocks until an item is added to blockingQueue
        // do something with item
    }
    // do others that might be blocking for a long time
}
我怀疑把它封闭在一个街区里可能有用。如果是一次性的,你可以

while (true)
{
    using (var item = blockingQueue.dequeue(); 
    {
        // do something with item
    }
    // do others that might be blocking for a long time
}
我可能误解了你的意思,但这里有另一种处理另一种情况的可能性:

while (true)
{
    var item = null;
    item = blockingQueue.dequeue(); // blocks until an item is added to blockingQueue
    // do something with item
    item = null;
}

如果处理完该项,可以在循环体的末尾释放对该项的引用:

item = null;
就垃圾收集而言,无论项目有多大,如果没有其他对它的引用,并且垃圾收集器没有收集它,那么垃圾收集器认为它还不需要收集


让垃圾收集器完成它的工作。它会在适当的时间收集东西,而且效率很高,在内存和时间之间进行权衡。

我认为问题在于
项在循环结束之前从不超出范围。GC不够聪明,无法识别
中的值在被覆盖之前不会被使用,因此无法收集该值


完成后,只需将其设置为
null
,即可删除最后一个引用并允许收集对象。

以下两个代码片段产生相同的il:

int i = 0;
System.Object x;
while(i < 100){
    x = new System.Object();
    System.Console.WriteLine(x.ToString());
    i++;
}
inti=0;
系统对象x;
而(i<100){
x=新的System.Object();
System.Console.WriteLine(x.ToString());
i++;
}
现在尝试依靠词法作用域释放x中的本地引用:

int i = 0;
while(i < 100){
    System.Object x = new System.Object();
    System.Console.WriteLine(x.ToString());
    i++;
}
inti=0;
而(i<100){
System.Object x=新的System.Object();
System.Console.WriteLine(x.ToString());
i++;
}
两种情况下的结果是相同的。循环迭代结束时,保存名为x的ref的局部变量不会为null。即使我们无法分支到循环的开始,local也永远不会设置为null。相反,当机会出现时,编译器将重用这个局部变量槽


如果显式地将x设置为null,编译器将发出il以将本地设置为null,即使启用了优化标志。如果这是优化出来的,它发生在JIT中,而不是静态编译器中。

抖动优化器可能是这个问题的根源。下面是一个例子:

class Program {
    static void Main(string[] args) {
        while (true) {
            var input = Console.ReadLine();
            Console.WriteLine(input);
            input = null;
        }
    }
}
生成此机器代码:

            while (true) {
                var input = Console.ReadLine();
00000000  push        ebp                    ; setup stack
00000001  mov         ebp,esp 
00000003  push        esi  
00000004  call        6E0208F0               ; Console.In property getter
00000009  mov         ecx,eax 
0000000b  mov         eax,dword ptr [ecx] 
0000000d  call        dword ptr [eax+64h]    ; TextReader.ReadLine()
00000010  mov         esi,eax                ; assign input variable
                Console.WriteLine(input);
00000012  call        6DB7BE38               ; Console.Out property getter
00000017  mov         ecx,eax
00000019  mov         edx,esi
0000001b  mov         eax,dword ptr [ecx] 
0000001d  call        dword ptr [eax+000000D8h] ; TextWriter.WriteLine()
00000023  jmp         00000004               ; repeat, note the missing null assigment
esi寄存器存储输入变量。请注意,它从未被设置回null,它始终存储对最后输入的字符串的引用。优化器已删除空赋值语句。垃圾回收器从抖动中获得生存期提示,它会说引用在循环期间是活动的

问题发生在第二次和后续过程中,当您从未键入某个内容时,ReadLine()将阻塞(类似于阻塞队列),esi寄存器值将继续引用该字符串。在循环期间,它永远不会被垃圾收集,至少在它被重新分配之前是这样

这个问题没有明确的解决办法。这是一个丑陋的例子:

    [MethodImpl(MethodImplOptions.NoInlining)]
    public static void NullReference<T>(ref T obj) where T : class {
        obj = null;
    }

其他东西是否有对它的引用?能否显示item的所有用法?探查器显示来自GC根的对对象的引用,这是其中之一。我在github上发布的示例显示了这个问题,其中唯一的引用是在循环中。@Simon:我之前说过这个,但是删除了它,但是,您用来重现这个问题的代码有问题。您可以从WinForms应用程序使用控制台,更重要的是,您可以锁定并等待UI线程。问题是你的程序甚至不再运行了,因为你正在等待一些不存在的线程来获取和释放锁。你有理由在意物品何时被GCed吗?另外,请确保您是在发布模式下编译而不是调试,在调试中,局部变量的生存期通常会延长,以使调试更容易。我很麻烦,因为在我的原因中,出列可能需要很长时间,并且该项包含一个大对象树的引用。此时,我能做的最好的事情就是调用
item.Dispose()
,希望它删除对其对象的引用(引用的类是第三方GUI组件,因此我无法控制它)。
        while (true) {
            var input = Console.ReadLine();
            Console.WriteLine(input);
            NullReference(ref input);
        }