我有一个递归函数,该函数具有许多占用大量内存的参考变量。我注意到每个堆栈帧大约占用1MB,因此当该函数被反复调用100次时,它将消耗100MB。
让我们看下面的简化示例。从main方法中,我调用了递归方法。对于每个堆栈帧,都会为堆上的新对象创建一个引用(“ someList”)。内存增加(图片上的ID 2,3)。然后,当弹出特定的堆栈帧时,释放内存(图片中的ID 4)。
我想做的是在弹出之前,为特定的堆栈框架处理创建的对象。 弹出新的堆栈帧时。
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);
}
}
答案 0 :(得分:0)
谢谢大家的帮助。正如@mjwills所写,将模式更改为Release 1会有所不同。我想与您分享我的观察。
从我的问题中获取代码,我们可以看到'someList'引用的对象没有在任何地方使用,CLR似乎可以理解它和该行
var someList = new List<string>();
但是,如果我这样修改代码...
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非常聪明,可以处理在先前堆栈框架中使用过的对象!堆的快照会产生以下结果:
答案 1 :(得分:-1)
您不能及早处置它们,因为当被呼叫者返回时,呼叫者将需要它们。这就是为什么将变量排列在堆栈中的原因。
如果在您的特定情况下,函数的编写方式是被调用者返回后调用者不需要变量,则您要查找的模式称为tail call optimization。不幸的是C#编译器doesn't do this for you automatically。但是您通常可以自己完成,方法是将递归算法更改为迭代算法,例如例如this poster或this one或this one。