【问题标题】:Does declaring Func<> in List.ForEach causing memory leak and increase process memory?在 List.ForEach 中声明 Func<> 会导致内存泄漏并增加进程内存吗?
【发布时间】:2021-02-24 03:20:04
【问题描述】:

我知道我不应该在 foreach 中声明 Func&lt;&gt;,但我想知道这段 C# 代码是否会增加进程内存并导致内存泄漏?

class Foo {
    int[] FooA = { 1, 2, 3, 4 };
    string result = "";
    void myMethod() {
        FooA.ToList().ForEach(a => {
            Func<int, string> myFunc = myFuncVar => $"Value: {myFuncVar}\n";
            result += myFunc(a);
        });
        MessageBox.Show(result);
    }
}

【问题讨论】:

  • 不会有内存泄漏,因为myFunc 没有离开作用域并且没有人引用它。在给定的情况下,myFunc 将被收集到 gen0(最频繁的收集)中,因此内存开销最小
  • @JL0PD 你的意思是如果我用其他方法调用myMethod(),不会有任何内存泄漏?
  • 您对“我不应该在foreach 中声明Func&lt;&gt;”的理由是什么?我不知道一个,所以想知道......(显然在这种特殊情况下它是无用的,但完全可以预期minimal reproducible example 严格说明问题并且不包含额外代码,如本文所示......所以你可能有其他原因)
  • @TCPW01,是的。但是,如果您想要确切的代码,最好使用string.Join
  • .NET 等托管框架本身没有内存泄漏。 IE。您可以“打包老鼠”并填充一堆您将永远不会在某处再次使用的引用并以这种方式浪费内存。但是您永远不会忘记对内存管理器无法摆脱的内存块的引用。

标签: c# memory-leaks func


【解决方案1】:

使用sharplab之类的反汇编工具很有帮助;

// trim
internal class Foo
{
    [Serializable]
    [CompilerGenerated]
    private sealed class <>c
    {
        public static readonly <>c <>9 = new <>c();

        public static Func<int, string> <>9__2_1;

        internal string <myMethod>b__2_1(int myFuncVar)
        {
            return string.Format("Value: {0}\n", myFuncVar);
        }
    }

    private int[] FooA;

    private string result;

    private void myMethod()
    {
        Enumerable.ToList(FooA).ForEach(new Action<int>(<myMethod>b__2_0));
    }

    public Foo()
    {
        int[] array = new int[4];
        RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
        FooA = array;
        result = "";
        base..ctor();
    }

    [CompilerGenerated]
    private void <myMethod>b__2_0(int a)
    {
        Func<int, string> func = <>c.<>9__2_1 ?? (<>c.<>9__2_1 = new Func<int, string>(<>c.<>9.<myMethod>b__2_1));
        result += func(a);
    }
}
// trim

您可以看到您的 lambda (myFuncVar =&gt; $"Value: {myFuncVar}\n";) 已提升为单例实例 (&lt;&gt;c.&lt;&gt;9) 上的方法 (&lt;myMethod&gt;b__2_1)。使用 Func 委托 (&lt;&gt;c.&lt;&gt;9__2_1) 的延迟初始化静态缓存。

所以不,至少在这种情况下,这里实际上没有每次迭代分配。

编译器可以证明您的 lambda 不需要对局部变量的引用,也不需要任何其他实例字段。即使您分配给该 lambda 的变量具有本地范围和生命周期,也可以将 lambda 本身提升为静态生命周期。类似于将字符串字面量分配给变量的方式,但字符串对象具有静态生命周期。

【讨论】:

    猜你喜欢
    • 2015-08-22
    • 1970-01-01
    • 2012-11-04
    • 1970-01-01
    • 2011-08-16
    • 2021-03-23
    • 2021-09-25
    • 2014-12-08
    • 1970-01-01
    相关资源
    最近更新 更多