谓词不释放内存

Amo*_*var 5 c# memory datagrid

如果我在内部包含外部引用,where predicate则内存不会获得释放.

List<object>如果我写where predicate这样的话,我想说我有一个:

    List<object> myList = new List<object>();
    ...
    myList.add(object);
    ...

    Expression<Func<object,bool>> predicate = p => myList.Contains(p);
Run Code Online (Sandbox Code Playgroud)

即使我做了myList = null或者predicate = null,它也没有释放记忆.

我已经List<object> itemsource绑定了DataGrid.我也使它的ItemSource为null,处理DataGrid,DataGrid为null..我还用ANTS Memory Profiler 7.4分析了这个问题.它也告诉我,因为wherepredicate它是持有参考.

如果我改变我wherepredicate像这样dispose(),那么内存得到释放.

    Expression<Func<object,bool>> predicate = p => p.id == 0;
Run Code Online (Sandbox Code Playgroud)

这意味着删除参考WherePredicate.

xan*_*tos 7

嗯......有趣......甚至Expression<>导致关闭......我不知道......

最终结果:谓词没有对myList的引用

我会解释一下:

private static bool IsDebug()
{
    // Taken from http://stackoverflow.com/questions/2104099/c-sharp-if-then-directives-for-debug-vs-release
    object[] customAttributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(DebuggableAttribute), false);

    if ((customAttributes != null) && (customAttributes.Length == 1))
    {
        DebuggableAttribute attribute = customAttributes[0] as DebuggableAttribute;
        return (attribute.IsJITOptimizerDisabled && attribute.IsJITTrackingEnabled);
    }

    return false;
}

static void Main(string[] args)
{
    // Check x86 or x64
    Console.WriteLine(IntPtr.Size == 4 ? "x86" : "x64");

    // Check Debug/Release
    Console.WriteLine(IsDebug() ? "Debug, USELESS BENCHMARK" : "Release");

    // Check if debugger is attached
    Console.WriteLine(System.Diagnostics.Debugger.IsAttached ? "Debugger attached, USELESS BENCHMARK!" : "Debugger not attached");

    Console.WriteLine();

    {
        long memory = GC.GetTotalMemory(true);

        // A big array, big enough that we can see its allocation in
        // memory
        byte[] buffer = new byte[10000000];

        Console.WriteLine("Just allocated the array: {0}", GC.GetTotalMemory(true) - memory);

        // A List<>, containing a reference to the buffer
        List<object> myList = new List<object>();
        myList.Add(buffer);

        Console.WriteLine("Added to the List<>: {0}", GC.GetTotalMemory(true) - memory);

        // We want to be sure that buffer is referenced at least up to
        // this point
        GC.KeepAlive(buffer);

        // But clearly setting buffer = null is useless, because the
        // List<> has anothe reference
        buffer = null;
        Console.WriteLine("buffer = null: {0}", GC.GetTotalMemory(true) - memory);

        // If I Clear() the List<>, the last reference to the buffer
        // is removed, and now the buffer can be freed
        myList.Clear();
        Console.WriteLine("myList.Clear(): {0}", GC.GetTotalMemory(true) - memory);

        GC.KeepAlive(myList);
    }

    Console.WriteLine();
    GC.Collect();

    {
        long memory = GC.GetTotalMemory(true);

        // A big array, big enough that we can see its allocation in
        // memory
        byte[] buffer = new byte[10000000];

        Console.WriteLine("Just allocated the array: {0}", GC.GetTotalMemory(true) - memory);

        // A List<>, containing a reference to the buffer
        List<object> myList = new List<object>();
        myList.Add(buffer);

        Console.WriteLine("Added to the List<>: {0}", GC.GetTotalMemory(true) - memory);

        // We want to be sure that buffer is referenced at least up to
        // this point
        GC.KeepAlive(buffer);

        // But clearly setting buffer = null is useless, because the
        // List<> has another reference
        buffer = null;
        Console.WriteLine("buffer = null: {0}", GC.GetTotalMemory(true) - memory);

        // We want to be sure that the List<> is referenced at least
        // up to this point
        GC.KeepAlive(myList);

        // If I set to null myList, the last reference to myList
        // and to buffer are removed
        myList = null;
        Console.WriteLine("myList = null: {0}", GC.GetTotalMemory(true) - memory);
    }

    Console.WriteLine();
    GC.Collect();

    {
        long memory = GC.GetTotalMemory(true);

        // A big array, big enough that we can see its allocation in
        // memory
        byte[] buffer = new byte[10000000];

        Console.WriteLine("Just allocated the array: {0}", GC.GetTotalMemory(true) - memory);

        // A List<>, containing a reference to the buffer
        List<object> myList = new List<object>();
        myList.Add(buffer);

        Console.WriteLine("Added to the List<>: {0}", GC.GetTotalMemory(true) - memory);

        // A predicate, containing a reference to myList
        Expression<Func<object, bool>> predicate1 = p => myList.Contains(p);
        Console.WriteLine("Created a predicate p => myList.Contains(p): {0}", GC.GetTotalMemory(true) - memory);

        // A second predicate, **not** containing a reference to
        // myList
        Expression<Func<object, bool>> predicate2 = p => p.GetHashCode() == 0;
        Console.WriteLine("Created a predicate p => p.GetHashCode() == 0: {0}", GC.GetTotalMemory(true) - memory);

        // We want to be sure that buffer is referenced at least up to
        // this point
        GC.KeepAlive(buffer);

        // But clearly setting buffer = null is useless, because the
        // List<> has another reference
        buffer = null;
        Console.WriteLine("buffer = null: {0}", GC.GetTotalMemory(true) - memory);

        // We want to be sure that the List<> is referenced at least
        // up to this point
        GC.KeepAlive(myList);

        // If I set to null myList, an interesting thing happens: the
        // memory is freed, even if the predicate1 is still alive!
        myList = null;
        Console.WriteLine("myList = null: {0}", GC.GetTotalMemory(true) - memory);

        // We want to be sure that the predicates are referenced at 
        // least up to this point
        GC.KeepAlive(predicate1);
        GC.KeepAlive(predicate2);

        try
        {
            // We compile the predicate1
            Func<object, bool> fn = predicate1.Compile();
            // And execute it!
            fn(5);
        }
        catch (NullReferenceException)
        {
            Console.WriteLine("predicate1 is 'pointing' to a null myList");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是一个三个部分的示例测试:基本点是byte[]分配了一个大数组,并通过检查分配了多少内存,我们检查数组是否仍以某种方式分配.在没有调试器(CTRL + F5)的发布模式下执行此代码非常重要.如果你不这样做,你会在程序启动时收到警告

前两个"部分"仅表示a List<>确实保持"活着"它引用的项目(byte[]在这种情况下是这样),并释放它List<>.Clear()让它收集GC byte[].

第三部分更有趣:既有a List<>又有Expression<>......两者似乎都是对它的引用byte[],但这是一种幻觉.该Expression<>书面导致编译器生成一个"封闭"周围的myList<>变量.使用ILSpy很容易看到:

Program.<>c__DisplayClassb <>c__DisplayClassb = new Program.<>c__DisplayClassb();
<>c__DisplayClassb.myList = new List<object>();
<>c__DisplayClassb.myList.Add(buffer3);

ParameterExpression parameterExpression = Expression.Parameter(typeof(object), "p");
Expression<Func<object, bool>> predicate = Expression.Lambda<Func<object, bool>>(Expression.Call(Expression.Field(Expression.Constant(<>c__DisplayClassb), fieldof(Program.<>c__DisplayClassb.myList)), methodof(List<object>.Contains(!0)), new Expression[]
{
    parameterExpression
}), new ParameterExpression[]
{
    parameterExpression
});
Run Code Online (Sandbox Code Playgroud)

(如果您没有ILSpy,您可以查看在线编译器TryRoslyn生成的代码,以获得更简单的示例)

<>c__DisplayClassb编译器使用字段生成隐藏类myList.因此myList,该方法不是具有"本地"变量,而是具有<>c__DisplayClassb字段的局部变量myList.在predicate1不直接保持为参考myList,但对变量的引用<>c__DisplayClassb(见Expression.Constant(<>c__DisplayClassb)?),所以当

<>c__DisplayClassb.myList = null;
Run Code Online (Sandbox Code Playgroud)

predicate1的确仍然有一个参考<>c__DisplayClassb,但<>c__DisplayClassb.myListnull,所以有没有更多的参考myList.

  • @AmolBavannavar您可以对谓词,列表(您说您有一个`DataGrid`),项目等进行多次引用,可能您显示的代码不是您正在运行的代码. (2认同)