【问题标题】:non executing linq causing memory allocation C#非执行 linq 导致内存分配 C#
【发布时间】:2015-01-28 15:56:30
【问题描述】:

在使用 Visual Studio 2013 性能向导分析我的代码的 .NET 内存分配时,我注意到某个函数分配了大量字节(因为它是在一个大循环中调用的)。但是查看分析报告中突出显示的函数,我完全不明白它为什么要分配任何内存。

为了更好地理解发生了什么,我隔离了导致分配的代码。这类似于下面的 LinqAllocationTester 类。

一旦我注释掉了该函数中的 LINQ 代码,该代码从未在测试代码路径中执行过,就不再分配内存。 NonLinqAllocationTester 类模仿这种行为。用普通循环替换 LINQ 代码也不会分配内存。

如果我在下面的测试代码上运行 .NET 内存分配测试,它显示 LinqAllocationTester 导致 100.000 次分配(每次调用 1 次),而 NonLinqAllocationTester 没有。 请注意,useLinq 始终为 false,因此 LINQ 代码本身从未真正执行过。

Function Name                    | Inclusive   | Exclusive   | Inclusive | Exclusive
                                 | Allocations | Allocations | Bytes     | Bytes
-------------------------------------------------------------------------------------
LinqAllocationTester.Test(int32) |     100.000 |     100.000 | 1.200.000 | 1.200.000
Program.Main(string[])           |     100.000 |           0 | 1.200.000 |         0

那么为什么不执行的 LINQ 代码会导致内存分配呢?除了避免使用 LINQ 函数之外,还有其他方法可以防止这种情况发生吗?

class Program {
    static void Main(string[] args) {
        List<int> values = new List<int>() { 1, 2, 3, 4 };
        LinqAllocationTester linqTester = new LinqAllocationTester(false, values);
        NonLinqAllocationTester nonLinqTester = new NonLinqAllocationTester(false, values);

        for (int i = 0; i < 100000; i++) {
            linqTester.MaxDifference(i);
        }

        for (int i = 0; i < 100000; i++) {
            nonLinqTester.MaxDifference(i);
        }
    }
}

internal class LinqAllocationTester {
    private bool useLinq;
    private List<int> values;

    public LinqAllocationTester(bool useLinq, List<int> values) {
        this.useLinq = useLinq;
        this.values = values;
    }

    public int MaxDifference(int value) {
        if (useLinq) {
            return values.Max(x => Math.Abs(value - x));
        } else {
            int maxDifference = int.MinValue;
            foreach (int value2 in values) {
                maxDifference = Math.Max(maxDifference, Math.Abs(value - value2));
            }
            return maxDifference;
        }
    }
}

internal class NonLinqAllocationTester {
    private bool useLinq;
    private List<int> values;

    public NonLinqAllocationTester(bool useLinq, List<int> values) {
        this.useLinq = useLinq;
        this.values = values;
    }

    public int MaxDifference(int value) {
        if (useLinq) {
            return 0;
        } else {
            int maxDifference = int.MinValue;
            foreach (int value2 in values) {
                maxDifference = Math.Max(maxDifference, Math.Abs(value - value2));
            }
            return maxDifference;
        }
    }
}

【问题讨论】:

    标签: c# .net linq memory-management visual-studio-2013


    【解决方案1】:

    您可以查看生成的 IL 以了解 LINQ 表达式的 DisplayClass 将在第一个 if 分支之外的方法的开头进行初始化。那是因为它在方法的开头(值首先出现的地方)为 lambda 表达式生成闭包。

    IL:

    IL_0000: ldnull
    IL_0001: stloc.2
    IL_0002: newobj instance void ConsoleApplication2.LinqAllocationTester/'<>c__DisplayClass2'::.ctor()
    IL_0007: stloc.3
    IL_0008: ldloc.3
    IL_0009: ldarg.1
    IL_000a: stfld int32 ConsoleApplication2.LinqAllocationTester/'<>c__DisplayClass2'::'value'
    IL_000f: nop
    IL_0010: ldarg.0
    IL_0011: ldfld bool ConsoleApplication2.LinqAllocationTester::useLinq
    IL_0016: ldc.i4.0
    IL_0017: ceq
    IL_0019: stloc.s CS$4$0001
    IL_001b: ldloc.s CS$4$0001
    IL_001d: brtrue.s IL_0042
    

    如果您将值复制到范围更窄的变量中,如下所示:

    if (useLinq)
    {
        int value2 = value;
        return values.Max(x => Math.Abs(value2 - x));
    }
    

    不应再发生额外的分配。

    【讨论】:

      猜你喜欢
      • 2019-08-12
      • 2011-02-18
      • 2020-11-18
      • 2020-04-06
      • 2018-07-12
      • 2013-10-15
      • 2015-12-15
      • 2018-12-13
      相关资源
      最近更新 更多