循环变量没有被收集

Sim*_*onC 9 c# scope loops

我有一个循环变量似乎没有收集垃圾(根据Red-Gate ANTS内存分析器),尽管已超出范围.

代码看起来像这样:

while (true)
{
    var item = blockingQueue.dequeue(); // blocks until an item is added to blockingQueue
    // do something with item
}
Run Code Online (Sandbox Code Playgroud)

据我所知,在返回item之前提到遗骸blockingQueue.dequeue().这是预期的行为,还是内存分析器中的错误?

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

请注意,探查器的文档说在执行内存快照之前执行了GC,并且引用不在终结器队列上.

我能够在这里用代码重现同样的问题.

更新

要点中的代码略有缺陷,因为它合法地保留了参考文献GetFoo().已经改变了它的对象时明确设置为现在得到收集null.但是,我相信汉斯的回答解释了我在实际代码中看到的情况.

Han*_*ant 7

抖动优化器可能是此问题的根源.这是一个例子:

class Program {
    static void Main(string[] args) {
        while (true) {
            var input = Console.ReadLine();
            Console.WriteLine(input);
            input = null;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

生成此机器代码:

            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
Run Code Online (Sandbox Code Playgroud)

esi寄存器存储输入变量.请注意它永远不会设置为null,它始终存储对最后输入的字符串的引用.优化器已删除null赋值语句.垃圾收集器从抖动中获取生命周期提示,它会说该引用在循环期间是活动的.

问题发生在第二次和后续的传递中,当你从不输入内容时,ReadLine()将阻塞(类似于阻塞队列),并且esi寄存器值继续引用该字符串.它永远不会在循环期间被垃圾收集,至少在它被重新分配之前.

对此没有干净的解决方法.这是一个丑陋的:

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

并使用:

        while (true) {
            var input = Console.ReadLine();
            Console.WriteLine(input);
            NullReference(ref input);
        }
Run Code Online (Sandbox Code Playgroud)