在弹出特定堆栈框架之前,先处理被引用的对象

时间:2018-08-06 21:48:20

标签: c# garbage-collection

我有一个递归函数,该函数具有许多占用大量内存的参考变量。我注意到每个堆栈帧大约占用1MB,因此当该函数被反复调用100次时,它将消耗100MB。

让我们看下面的简化示例。从main方法中,我调用了递归方法。对于每个堆栈帧,都会为堆上的新对象创建一个引用(“ someList”)。内存增加(图片上的ID 2,3)。然后,当弹出特定的堆栈帧时,释放内存(图片中的ID 4)。

enter image description here

我想做的是在弹出之前,为特定的堆栈框架处理创建的对象。 弹出新的堆栈帧时。

class Program
{
    static void Main(string[] args)
    {
        var testClass = new TestHeapCollection();

        Console.WriteLine(testClass.IsSaved(10));
        Console.ReadKey();
    }
}

internal class TestHeapCollection
{

    public bool IsSaved(int a)
    {
        var someList = new List<string>();

        /*               
            processing data with local variables
        */

        return a <= 0 || IsSaved(a - 1);
    }
}

2 个答案:

答案 0 :(得分:0)

谢谢大家的帮助。正如@mjwills所写,将模式更改为Release 1会有所不同。我想与您分享我的观察。

从我的问题中获取代码,我们可以看到'someList'引用的对象没有在任何地方使用,CLR似乎可以理解它和该行

 var someList = new List<string>();

不创建任何对象。堆的大小不变。 enter image description here

但是,如果我这样修改代码...

internal class TestHeapCollection
{

    //stackoverflowQuestion
    public bool IsSaved(int a)
    {
        var someList = new List<int> {a-1};
        /*

        processing data with local variables

        */

        return a <= 0 || IsSaved(someList[0]);
    }
}

..创建对象,并将列表的值传递到弹出的下一个堆栈框架。后来, GC非常聪明,可以处理在先前堆栈框架中使用过的对象!堆的快照会产生以下结果:

enter image description here

答案 1 :(得分:-1)

您不能及早处置它们,因为当被呼叫者返回时,呼叫者将需要它们。这就是为什么将变量排列在堆栈中的原因。

如果在您的特定情况下,函数的编写方式是被调用者返回后调用者不需要变量,则您要查找的模式称为tail call optimization。不幸的是C#编译器doesn't do this for you automatically。但是您通常可以自己完成,方法是将递归算法更改为迭代算法,例如例如this posterthis onethis one