【问题标题】:What is the impact of C# closures on memory?C#闭包对内存有什么影响?
【发布时间】:2017-08-08 15:51:50
【问题描述】:

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

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
}

【问题讨论】:

  • 不是答案,但myInt = 1 应该怎么做? lambdas 不接受 ref 参数,所以这个赋值将没有效果。
  • "它会将整个 Test 对象保存在其环境中,还是仅保存 Test.b?"两者都不是,它只是持有一个对test的引用。
  • 我做了一个最小的例子,我忘记了 ref,在我的代码中它是一个我要修改的列表,让我更正一下
  • 如果您发布实际代码而不是无法编译的代码,并且在编写该代码时也遵循 .NET 命名约定,这将非常有帮助。跨度>
  • @RenéVogt 一个 lambda 可以接受 ref 参数,尽管那个特定的参数没有。可以写成var lambda = (ref myInt) =>[...]。

标签: c# lambda closures


【解决方案1】:

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

好吧,它将捕获变量 test(通过创建一个单独的类来包含该变量),该变量又具有一个对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 的值本身发生变化(例如,引用 Test 的不同实例),您将不会再在委托中看到 那个

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 老兄,你刚刚击败了 Jon Skeet。恭喜! ;)
    猜你喜欢
    • 1970-01-01
    • 2020-11-24
    • 2012-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-04
    相关资源
    最近更新 更多