C#闭包对内存有什么影响?

Ant*_* C. 2 c# lambda closures

我有一个类Test,它包含两个成员,一个(arr)占用大量内存,另一个(b)不占用:

public class Test
{
    public Test() {
        Arr = new int[100000000];
    }
    public bool B {get; private set;}
    public int[] Arr {get; private set;}
} 
Run Code Online (Sandbox Code Playgroud)

稍后在我的代码中,我想以这种方式存储lambda表达式:

// `test` has been declared somewhere as an instance of Test
Action lambda = () => {
    if (test.B)
        // Do things
}
Run Code Online (Sandbox Code Playgroud)

这个闭包的内存消耗是多少?

它会将整个Test物体保持在其环境中,还是只保留Test.b

我应该这样做:

var tmpB = test.B;
Action lambda = () => {
    if (tmpB)
        // Do things
}
Run Code Online (Sandbox Code Playgroud)

Ser*_*rvy 5

闭包将存储test变量的值,变量test只是Test内存中其他类型的对象的引用,因为它不是a struct,并且该Test对象实际上没有整数数组,它只是有一个引用存储在内存中另一个位置的大型数组.

由于您持有对该实例的引用,Test只要该闭包不符合垃圾回收条件,该对象就不符合垃圾回收的条件.如果你拉布尔值出的Test对象,然后关闭了,当你表现,那么你就不再引用的Test对象.因此,如果没有任何东西可以访问Test实例或包含的数组,那么它就有资格进行垃圾回收.如果仍然有其他代码可以访问它,那就不是这种情况,并且没有任何好处.

  • 伙计,你刚刚击败了乔恩斯基特。恭喜!;) (2认同)

Jon*_*eet 5

它会将整个Test对象保存在其环境中,还是只保存Test.b?

好吧,它将捕获变量 test(通过创建一个单独的类来包含该变量),而后者又具有一个值,该值是对实例的引用Test.

换句话说,这样的方法:

public Action Foo()
{
    Test test = new Test();
    Action printB = () => Console.WriteLine(test.b);
    return printB;
}
Run Code Online (Sandbox Code Playgroud)

将被转换成这样的东西:

public Action Foo()
{
    CompiledGeneratedClass tmp = new CompilerGEneratedClass();
    tmp.test = new Test();
    Action printB = tmp.GeneratedMethod;
    return printB;
}

private class CompilerGeneratedClass
{
    public Test test;

    public void GeneratedMethod()
    {
        Console.WriteLine(test.b)
    }
}
Run Code Online (Sandbox Code Playgroud)

所以是的,如果你不希望委托有效地保持Test活着的实例,你应该首先提取属性的值.请注意,这有两个语义差异:

  • 如果属性的值在对象中发生更改,则您将不再在委托中看到该值
  • 如果test自身的值发生变化(例如,引用不同的实例Test),您将不再在委托中看到