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

时间:2017-08-08 15:51:51

标签: c# lambda closures

我有一个班级Test,其中包含两个成员,一个(arr),其中大量内存,另一个(b) #39; T:

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

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

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

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

它是否会在其环境中保留整个Test对象,还是只保留Test.b

我应该这样做:

var tmpB = test.B;
Action lambda = () => {
    if (tmpB)
        // Do things
}

2 个答案:

答案 0 :(得分:5)

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

由于您持有对该Test实例的引用,只要该关闭不符合垃圾回收条件,该对象就无法进行垃圾回收。如果您从Test对象中拉出布尔值并关闭 ,如您所示,则您不再引用Test对象。因此,如果没有任何东西可以访问Test实例或包含的数组,那么它就有资格进行垃圾回收。如果仍然有其他代码可以访问它,那么情况就不会如此,并且没有任何好处。

答案 1 :(得分:5)

  

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

好吧,它会捕获变量 test(通过创建一个单独的类来包含该变量),而后者又有一个值,它是{{{{{ 1}}。

换句话说,这样的方法:

Test

将转换为这样的内容:

public Action Foo()
{
    Test test = new Test();
    Action printB = () => Console.WriteLine(test.b);
    return printB;
}

所以是的,如果你不希望委托有效地保持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) } } 的实例存活,你应该首先提取属性的值。请注意,这有两个语义差异:

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