如何避免堆栈溢出?
我使用 CSharpCodeProvider 编译代码,并在结果程序集中动态创建某个类的实例。比我调用一些方法。如果该方法有递归,我会得到 StackOverflowException 并且我的应用程序终止。
我该如何避免这种情况?
using System;
using System.Runtime.Remoting;
namespace TestStackOverflow
{
class Program
{
class StackOver : MarshalByRefObject
{
public void Run()
{
Run();
}
}
static void Main(string[] args)
{
AppDomain domain = AppDomain.CreateDomain("new");
ObjectHandle handle = domain.CreateInstance(typeof (StackOver).Assembly.FullName, typeof (StackOver).FullName);
if (handle != null)
{
StackOver stack = (StackOver) handle.Unwrap();
stack.Run();
}
}
}
}
有关的:
I compile my code using CSharpCodeProvider, and dynamically create instance of some class in result assembly. Than I call some method. If the method has recursion I get StackOverflowException and my app terminates.
How do I avoid this?
using System;
using System.Runtime.Remoting;
namespace TestStackOverflow
{
class Program
{
class StackOver : MarshalByRefObject
{
public void Run()
{
Run();
}
}
static void Main(string[] args)
{
AppDomain domain = AppDomain.CreateDomain("new");
ObjectHandle handle = domain.CreateInstance(typeof (StackOver).Assembly.FullName, typeof (StackOver).FullName);
if (handle != null)
{
StackOver stack = (StackOver) handle.Unwrap();
stack.Run();
}
}
}
}
Related:
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
StackOverflow 表明您的递归太深并且堆栈内存不足。例如:
这将导致堆栈溢出,因为 StackOver::Run() 将被一遍又一遍地调用,直到没有剩余内存为止。
我怀疑在你的情况下,你可能缺少终止条件或者你运行了太多的递归迭代。
如果您尝试保持应用程序运行,请尝试:
StackOverflow indicates that your recursion is going too deep and the stack is running out of memory. For example:
This will result in a stack overflow because StackOver::Run() will be called over and over until there is no memory left.
I suspect in your case, you may be missing a termination condition or you are running too many recursion iterations.
If you are trying to keep the application running, try:
Run正在调用Run。这就是无限递归。
Run is calling Run. That is the infinite recursion.
如果递归导致堆栈溢出,那么问题与编译类无关——递归函数需要终止条件,因为 C#(通常)不会优化尾部调用。
If recursion causes a stack overflow, then the problem is not related to compiling the class -- a recursive function needs a terminating condition, because C# doesn't (usually) optimize tail calls.
避免递归函数堆栈溢出的唯一方法是有一个明确的退出条件,无论输入如何,最终都会满足该条件。您可以定义最大深度并在达到该深度后停止进行递归调用,或者确保您检查的数据是有限的(并且在合理的限制内),或者两者的组合。
The only way to avoid stack overflows with recursive functions is to have a clear exit condition that will eventually be met regardless of the input. Either you define a maximum depth and stop making recursive calls once you reach it, or you make sure that the data that you examine is finite (and within reasonable limits), or a combination of both.
每次从方法 bar 调用方法 foo 时,bar 都会添加到调用堆栈中。调用堆栈用于跟踪调用方法之前代码所在的位置,以便在 foo 完成时可以返回那里。
以下递归函数
在多次递归调用 Factorial(5) 后,调用堆栈将如下所示:
此时 n 为 1,因此函数停止调用递归情况并返回 1。然后程序开始回溯调用堆栈,整个过程返回 120。
如果没有调用堆栈,程序在执行完方法后将不知道要返回到哪里。
现在假设基本情况不存在,它看起来像这样:
在调用 Factorial(5) 的几次递归之后,调用堆栈将如下所示:
因为代码没有停止调用自身的点,所以它会一直这样下去,调用堆栈会越来越大,占用越来越多的内存,直到超过分配的内存,并抛出 StackOverflow 异常。
有两种方法可以阻止这种情况发生,最好的方法取决于具体情况。
1 提供基本案例。确保最终达到某个条件,阻止函数调用自身。在阶乘的情况下,n == 1,但也可能是已经过去了一定的时间,已经递归了一定的次数,某些计算的某些结果在某些范围内,等等。只要在堆栈太大之前停止回收即可。
2 删除递归并重新编写它。任何递归算法都可以重写为非递归算法。它可能不那么干净和优雅,但它是可以做到的。在阶乘参数中,它可能类似于:
如果目标是一次又一次地不断运行相同的函数,那么您可以将递归重写
为
Every time you call a method foo from method bar, bar is added to the call stack. The call stack is used to keep track of where the code was before the method was called so it can return there when foo is done.
The following recursive function
after several recursions of the call Factorial(5) the call stack would look like this:
At this point n is 1, and so the function stops calling the recursive case and instead returns 1. The program then starts winding back up the call stack and the whole thing returns 120.
Without the call stack the program wouldn't know where to go back to when it had finished executing a method.
Now suppose that base case wasn't there, and it was just looked like this:
After several recursions of the call Factorial(5) the call stack would look like this:
Because there is no point at which the code stops calling itself it will carry on forever, and the call stack will grow and grow and grow taking up more and more memory until it exceeds the memory it has been allocated and the StackOverflow exception is thrown.
There are 2 ways to stop this from happening, the best depends on the situation.
1 Provide a base case. Make sure there is some condition that is eventually reached which stops the function from calling itself. In the Factorial case it's that n == 1, but it could be that a certain amount of time has passed, that it has recursed a certain number of times, that some result of some computation is within some bounds, whatever. As long as it stops recusing before the stack is too big.
2 Remove the recursion and re-write it without. Any recursive algorithm can be re-written as a non-recursive algorithm. It may not be as clean and elegant, but it can be done. In the factorial argument it may be something like:
If the aim is to continually run the same function again and again, then you can re-write the recursive
as
我没有很好的 CSharpCodeProvider 背景,但我知道递归实现的算法可以用循环来实现
I dont have a good background of CSharpCodeProvider but I know that recursively implemented algorithm could be implemented with a loop
好的。使用或不使用 CSharpCodeProvider 并不重要。我正在另一个域中使用反射加载程序集。我认为域是出于安全原因而创建的。我怎样才能防止应用程序终止???
Ok. It doesn't matter use CSharpCodeProvider or not. I'm loading assembly using Reflection in another domain. I think domains was created for security reason. How can I safe the app from terminating???